From 36d9d84bc228150599d7a1313ff792e1571ec607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Mon, 13 Oct 2025 15:13:45 +0200 Subject: [PATCH 01/26] Refactor WellGroupHelpers Use a dynamic WellGroupHelper object that can store state instead of the current WellGroupHelpers collection of static functions. This is in preparation for the reserovir coupling feature which needs to be injected into the well group helper functions. --- CMakeLists_files.cmake | 2 + opm/simulators/wells/BlackoilWellModel.hpp | 3 +- .../wells/BlackoilWellModelConstraints.cpp | 243 +- .../wells/BlackoilWellModelConstraints.hpp | 16 +- .../wells/BlackoilWellModelGasLift_impl.hpp | 3 +- .../wells/BlackoilWellModelGeneric.cpp | 348 +-- .../wells/BlackoilWellModelGeneric.hpp | 12 +- .../wells/BlackoilWellModelGuideRates.cpp | 11 +- .../wells/BlackoilWellModelGuideRates.hpp | 5 + .../wells/BlackoilWellModelNldd_impl.hpp | 6 +- .../wells/BlackoilWellModel_impl.hpp | 79 +- opm/simulators/wells/FractionCalculator.cpp | 33 +- opm/simulators/wells/FractionCalculator.hpp | 11 +- .../wells/GasLiftSingleWell_impl.hpp | 2 + opm/simulators/wells/MultisegmentWell.hpp | 19 +- .../wells/MultisegmentWell_impl.hpp | 72 +- opm/simulators/wells/StandardWell.hpp | 17 +- opm/simulators/wells/StandardWell_impl.hpp | 60 +- opm/simulators/wells/WellGroupConstraints.cpp | 78 +- opm/simulators/wells/WellGroupConstraints.hpp | 16 +- opm/simulators/wells/WellGroupControls.cpp | 17 +- opm/simulators/wells/WellGroupControls.hpp | 7 +- opm/simulators/wells/WellGroupHelper.cpp | 1992 +++++++++++++++++ opm/simulators/wells/WellGroupHelper.hpp | 386 ++++ opm/simulators/wells/WellGroupHelpers.cpp | 676 ------ opm/simulators/wells/WellGroupHelpers.hpp | 63 - opm/simulators/wells/WellInterface.hpp | 52 +- .../wells/WellInterfaceFluidSystem.cpp | 16 +- .../wells/WellInterfaceFluidSystem.hpp | 8 +- opm/simulators/wells/WellInterface_impl.hpp | 150 +- 30 files changed, 2927 insertions(+), 1476 deletions(-) create mode 100644 opm/simulators/wells/WellGroupHelper.cpp create mode 100644 opm/simulators/wells/WellGroupHelper.hpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index d31905819c..4941498539 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -243,6 +243,7 @@ list (APPEND MAIN_SOURCE_FILES opm/simulators/wells/WellFilterCake.cpp opm/simulators/wells/WellGroupConstraints.cpp opm/simulators/wells/WellGroupControls.cpp + opm/simulators/wells/WellGroupHelper.cpp opm/simulators/wells/WellGroupHelpers.cpp opm/simulators/wells/WellHelpers.cpp opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -1158,6 +1159,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/simulators/wells/WellFilterCake.hpp opm/simulators/wells/WellGroupConstraints.hpp opm/simulators/wells/WellGroupControls.hpp + opm/simulators/wells/WellGroupHelper.hpp opm/simulators/wells/WellGroupHelpers.hpp opm/simulators/wells/WellHelpers.hpp opm/simulators/wells/WellInterfaceFluidSystem.hpp diff --git a/opm/simulators/wells/BlackoilWellModel.hpp b/opm/simulators/wells/BlackoilWellModel.hpp index 60feb4ed64..c69a2df587 100644 --- a/opm/simulators/wells/BlackoilWellModel.hpp +++ b/opm/simulators/wells/BlackoilWellModel.hpp @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -115,8 +116,8 @@ template class WellContributions; using ModelParameters = BlackoilModelParameters; using WellConnectionModule = WellConnectionAuxiliaryModule>; - using IndexTraits = typename FluidSystem::IndexTraitsType; + using WellGroupHelperType = WellGroupHelper; constexpr static std::size_t pressureVarIndex = GetPropType::pressureSwitchIdx; diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index 4ec89cc128..50e20a5d52 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -46,7 +45,6 @@ checkGroupInjectionConstraints(const Group& group, const int reportStepIdx, const Phase& phase) const { - const auto& well_state = wellModel_.wellState(); const auto& pu = wellModel_.phaseUsage(); int phasePos; @@ -65,13 +63,7 @@ checkGroupInjectionConstraints(const Group& group, if (currentControl != Group::InjectionCMode::RATE) { Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - phasePos, - /*isInjector*/true, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellSurfaceRates(group, phasePos, /*isInjector*/true); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -95,13 +87,7 @@ checkGroupInjectionConstraints(const Group& group, if (currentControl != Group::InjectionCMode::RESV) { Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - phasePos, - /*isInjector*/true, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellResRates(group, phasePos, /*is_injector=*/true); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -126,25 +112,13 @@ checkGroupInjectionConstraints(const Group& group, Scalar production_Rate = 0.0; const auto& controls = group.injectionControls(phase, wellModel_.summaryState()); const Group& groupRein = wellModel_.schedule().getGroup(controls.reinj_group, reportStepIdx); - production_Rate += WellGroupHelpersType::sumWellSurfaceRates(groupRein, - wellModel_.schedule(), - well_state, - reportStepIdx, - phasePos, - /*isInjector*/false, - wellModel_.summaryState()); + production_Rate += wgHelper().sumWellSurfaceRates(groupRein, phasePos, /*is_injector=*/false); // sum over all nodes production_Rate = wellModel_.comm().sum(production_Rate); Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - phasePos, - /*isInjector*/true, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellSurfaceRates(group, phasePos, /*is_injector=*/true); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -164,52 +138,29 @@ checkGroupInjectionConstraints(const Group& group, Scalar voidage_rate = 0.0; const auto& controls = group.injectionControls(phase, wellModel_.summaryState()); const Group& groupVoidage = wellModel_.schedule().getGroup(controls.voidage_group, reportStepIdx); - voidage_rate += WellGroupHelpersType::sumWellResRates(groupVoidage, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(waterPhaseIdx), - false, - wellModel_.summaryState()); - voidage_rate += WellGroupHelpersType::sumWellResRates(groupVoidage, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(oilPhaseIdx), - false, - wellModel_.summaryState()); - voidage_rate += WellGroupHelpersType::sumWellResRates(groupVoidage, - wellModel_.schedule(), - well_state, reportStepIdx, - pu.canonicalToActivePhaseIdx(gasPhaseIdx), - false, - wellModel_.summaryState()); + voidage_rate += wgHelper().sumWellResRates(groupVoidage, + pu.canonicalToActivePhaseIdx(waterPhaseIdx), + /*is_injector=*/false); + voidage_rate += wgHelper().sumWellResRates(groupVoidage, + pu.canonicalToActivePhaseIdx(oilPhaseIdx), + /*is_injector=*/false); + voidage_rate += wgHelper().sumWellResRates(groupVoidage, + pu.canonicalToActivePhaseIdx(gasPhaseIdx), + /*is_injector=*/false); // sum over all nodes voidage_rate = wellModel_.comm().sum(voidage_rate); Scalar total_rate = 0.0; - total_rate += WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(waterPhaseIdx), - true, - wellModel_.summaryState()); - total_rate += WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(oilPhaseIdx), - true, - wellModel_.summaryState()); - total_rate += WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(gasPhaseIdx), - true, - wellModel_.summaryState()); + total_rate += wgHelper().sumWellResRates(group, + pu.canonicalToActivePhaseIdx(waterPhaseIdx), + /*is_injector=*/true); + total_rate += wgHelper().sumWellResRates(group, + pu.canonicalToActivePhaseIdx(oilPhaseIdx), + /*is_injector=*/true); + total_rate += wgHelper().sumWellResRates(group, + pu.canonicalToActivePhaseIdx(gasPhaseIdx), + /*is_injector=*/true); // sum over all nodes total_rate = wellModel_.comm().sum(total_rate); @@ -228,12 +179,8 @@ checkGroupInjectionConstraints(const Group& group, template std::pair BlackoilWellModelConstraints:: -checkGroupProductionConstraints(const Group& group, - const int reportStepIdx, - DeferredLogger& deferred_logger) const +checkGroupProductionConstraints(const Group& group, DeferredLogger& deferred_logger) const { - const auto& well_state = wellModel_.wellState(); - const auto controls = group.productionControls(wellModel_.summaryState()); const Group::ProductionCMode& currentControl = wellModel_.groupState().production_control(group.name()); const auto& pu = wellModel_.phaseUsage(); @@ -242,13 +189,9 @@ checkGroupProductionConstraints(const Group& group, if (currentControl != Group::ProductionCMode::ORAT) { Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(oilPhaseIdx), - false, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(oilPhaseIdx), + /*is_injector=*/false); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -267,13 +210,9 @@ checkGroupProductionConstraints(const Group& group, if (currentControl != Group::ProductionCMode::WRAT) { Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(waterPhaseIdx), - false, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(waterPhaseIdx), + /*is_injector=*/false); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -291,13 +230,9 @@ checkGroupProductionConstraints(const Group& group, if (currentControl != Group::ProductionCMode::GRAT) { Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(gasPhaseIdx), - false, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(gasPhaseIdx), + /*is_injector=*/false); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -314,20 +249,15 @@ checkGroupProductionConstraints(const Group& group, if (currentControl != Group::ProductionCMode::LRAT) { Scalar current_rate = 0.0; - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(oilPhaseIdx), - false, - wellModel_.summaryState()); - current_rate += WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(waterPhaseIdx), - false, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(oilPhaseIdx), + /*is_injector=*/false); + current_rate += wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(waterPhaseIdx), + /*is_injector=*/false); + current_rate += wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(gasPhaseIdx), + /*is_injector=*/false); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -335,13 +265,10 @@ checkGroupProductionConstraints(const Group& group, bool skip = false; if (controls.liquid_target == controls.oil_target) { Scalar current_water_rate = - WellGroupHelpersType::sumWellSurfaceRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(waterPhaseIdx), - false, - wellModel_.summaryState()); + wgHelper().sumWellSurfaceRates(group, + pu.canonicalToActivePhaseIdx(waterPhaseIdx), + /*is_injector=*/false); + current_water_rate = wellModel_.comm().sum(current_water_rate); current_water_rate = wellModel_.comm().sum(current_water_rate); if (std::abs(current_water_rate) < 1e-12) { skip = true; @@ -367,30 +294,15 @@ checkGroupProductionConstraints(const Group& group, if (currentControl != Group::ProductionCMode::RESV) { Scalar current_rate = 0.0; - current_rate += - WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(waterPhaseIdx), - false, - wellModel_.summaryState()); - current_rate += - WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(oilPhaseIdx), - false, - wellModel_.summaryState()); - current_rate += - WellGroupHelpersType::sumWellResRates(group, - wellModel_.schedule(), - well_state, - reportStepIdx, - pu.canonicalToActivePhaseIdx(gasPhaseIdx), - false, - wellModel_.summaryState()); + current_rate += wgHelper().sumWellResRates(group, + pu.canonicalToActivePhaseIdx(waterPhaseIdx), + /*is_injector=*/false); + current_rate += wgHelper().sumWellResRates(group, + pu.canonicalToActivePhaseIdx(oilPhaseIdx), + /*is_injector=*/false); + current_rate += wgHelper().sumWellResRates(group, + pu.canonicalToActivePhaseIdx(gasPhaseIdx), + /*is_injector=*/false); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); @@ -434,9 +346,7 @@ checkGroupConstraints(const Group& group, } } if (group.isProductionGroup()) { - const auto& check = this->checkGroupProductionConstraints(group, - reportStepIdx, - deferred_logger); + const auto& check = this->checkGroupProductionConstraints(group, deferred_logger); if (check.first != Group::ProductionCMode::NONE) { return true; @@ -478,10 +388,8 @@ actionOnBrokenConstraints(const Group& group, template bool BlackoilWellModelConstraints:: actionOnBrokenConstraints(const Group& group, - const int reportStepIdx, const Group::GroupLimitAction group_limit_action, const Group::ProductionCMode& newControl, - const WellState& well_state, std::optional& worst_offending_well, GroupState& group_state, DeferredLogger& deferred_logger) const @@ -548,9 +456,7 @@ actionOnBrokenConstraints(const Group& group, } case Group::ExceedAction::WELL: { std::tie(worst_offending_well, std::ignore) = - WellGroupHelpersType::worstOffendingWell(group, wellModel_.schedule(), reportStepIdx, - newControl, - wellModel_.comm(), well_state, deferred_logger); + wgHelper().worstOffendingWell(group, newControl, wellModel_.comm()); break; } case Group::ExceedAction::PLUG: { @@ -592,8 +498,7 @@ updateGroupIndividualControl(const Group& group, std::map>& switched_prod, std::map>& closed_offending_wells, GroupState& group_state, - WellState& well_state, - DeferredLogger& deferred_logger) const + DeferredLogger& deferred_logger) { bool changed = false; if (group.isInjectionGroup()) @@ -642,13 +547,9 @@ updateGroupIndividualControl(const Group& group, } this->actionOnBrokenConstraints(group, changed_this.first, phase, group_state, deferred_logger); - WellGroupHelpersType::updateWellRatesFromGroupTargetScale(changed_this.second, - group, - wellModel_.schedule(), - reportStepIdx, - /* isInjector */ false, - wellModel_.groupState(), - well_state); + wgHelper().updateWellRatesFromGroupTargetScale(changed_this.second, + group, + /*is_injector=*/false); changed = true; } } @@ -676,32 +577,26 @@ updateGroupIndividualControl(const Group& group, } } - const auto& changed_this = this->checkGroupProductionConstraints(group, - reportStepIdx, - deferred_logger); + const auto& changed_this = this->checkGroupProductionConstraints(group, deferred_logger); const auto controls = group.productionControls(wellModel_.summaryState()); if (changed_this.first != Group::ProductionCMode::NONE) { std::optional worst_offending_well = std::nullopt; - changed = this->actionOnBrokenConstraints(group, reportStepIdx, - controls.group_limit_action, - changed_this.first, well_state, - worst_offending_well, - group_state, deferred_logger); + changed = this->actionOnBrokenConstraints(group, + controls.group_limit_action, + changed_this.first, + worst_offending_well, + group_state, deferred_logger); if(changed) { if (update_group_switching_log || switched_prod[group.name()].empty()) { switched_prod[group.name()].push_back(currentControl); } - WellGroupHelpersType::updateWellRatesFromGroupTargetScale(changed_this.second, - group, - wellModel_.schedule(), - reportStepIdx, - /* isInjector */ false, - wellModel_.groupState(), - well_state); + wgHelper().updateWellRatesFromGroupTargetScale(changed_this.second, + group, + /*is_injector=*/false); } else if (worst_offending_well) { closed_offending_wells.insert_or_assign(group.name(), std::make_pair(Group::ProductionCMode2String(changed_this.first), *worst_offending_well)); diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.hpp b/opm/simulators/wells/BlackoilWellModelConstraints.hpp index 3ee5b1942f..466f923a33 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.hpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.hpp @@ -34,7 +34,7 @@ class DeferredLogger; template class GroupState; class SummaryState; template class WellState; -template class WellGroupHelpers; +template class WellGroupHelper; /// Class for handling constraints for the blackoil well model. template @@ -46,10 +46,10 @@ public: constexpr static int oilPhaseIdx = IndexTraits::oilPhaseIdx; constexpr static int gasPhaseIdx = IndexTraits::gasPhaseIdx; - using WellGroupHelpersType = WellGroupHelpers; + using WellGroupHelperType = WellGroupHelper; //! \brief Constructor initializes reference to the well model. - explicit BlackoilWellModelConstraints(const BlackoilWellModelGeneric& wellModel) + explicit BlackoilWellModelConstraints(BlackoilWellModelGeneric& wellModel) : wellModel_(wellModel) {} @@ -67,10 +67,8 @@ public: //! \brief Execute action on broken constraint for a production well group. Return true if a group control is changed bool actionOnBrokenConstraints(const Group& group, - const int reportStepIdx, const Group::GroupLimitAction group_limit_action, const Group::ProductionCMode& newControl, - const WellState& well_state, std::optional& worst_offending_well, GroupState& group_state, DeferredLogger& deferred_logger) const; @@ -84,8 +82,7 @@ public: std::map>& switched_prod, std::map>& closed_offending_wells, GroupState& group_state, - WellState& well_state, - DeferredLogger& deferred_logger) const; + DeferredLogger& deferred_logger); private: //! \brief Check and return value and type of constraints for an injection well group. @@ -97,10 +94,11 @@ private: //! \brief Check and return value and type of constraints for a production well group. std::pair checkGroupProductionConstraints(const Group& group, - const int reportStepIdx, DeferredLogger& deferred_logger) const; - const BlackoilWellModelGeneric& wellModel_; //!< Reference to well model + WellGroupHelperType& wgHelper() { return wellModel_.wgHelper(); } + const WellGroupHelperType& wgHelper() const { return wellModel_.wgHelper(); } + BlackoilWellModelGeneric& wellModel_; //!< Reference to well model }; } // namespace Opm diff --git a/opm/simulators/wells/BlackoilWellModelGasLift_impl.hpp b/opm/simulators/wells/BlackoilWellModelGasLift_impl.hpp index 7b05b9cd6b..ff6e5aa773 100644 --- a/opm/simulators/wells/BlackoilWellModelGasLift_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModelGasLift_impl.hpp @@ -334,7 +334,8 @@ updateWellPotentials(const Simulator& simulator, auto cur_exc_type = ExceptionType::NONE; try { std::vector potentials; - well->computeWellPotentials(simulator, well_state_copy, potentials, deferred_logger); + const auto& wgHelper = simulator.problem().wellModel().wgHelper(); + well->computeWellPotentials(simulator, well_state_copy, wgHelper, potentials, deferred_logger); auto& ws = wellState.well(well->indexOfWell()); for (int p = 0; p < np; ++p) { // make sure the potentials are positive diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 0fa8dfb5d3..dbf2794da5 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,7 @@ BlackoilWellModelGeneric(Schedule& schedule, , active_wgstate_(pu) , last_valid_wgstate_(pu) , nupcol_wgstate_(pu) + , wg_helper_(schedule, summaryState, guideRate_, pu) { const auto numProcs = comm_.size(); @@ -505,20 +507,12 @@ checkGconsaleLimits(const Group& group, const Group::ProductionCMode& oldProductionControl = this->groupState().production_control(group.name()); const int gasPos = phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - Scalar production_rate = WellGroupHelpersType::sumWellSurfaceRates(group, - schedule(), - well_state, - reportStepIdx, - gasPos, - /*isInjector*/false, - this->summaryState_); - Scalar injection_rate = WellGroupHelpersType::sumWellSurfaceRates(group, - schedule(), - well_state, - reportStepIdx, - gasPos, - /*isInjector*/true, - this->summaryState_); + Scalar production_rate = this->wgHelper().sumWellSurfaceRates( + group, gasPos, /*is_injector=*/false + ); + Scalar injection_rate = this->wgHelper().sumWellSurfaceRates( + group, gasPos, /*is_injector=*/true + ); // sum over all nodes injection_rate = comm_.sum(injection_rate); production_rate = comm_.sum(production_rate); @@ -655,13 +649,9 @@ checkGroupHigherConstraints(const Group& group, const std::vector reduction_rates = this->groupState().injection_reduction_rates(group.name()); for (int phasePos = 0; phasePos < this->numPhases(); ++phasePos) { - const Scalar local_current_rate = WellGroupHelpersType::sumWellSurfaceRates(group, - schedule(), - this->wellState(), - reportStepIdx, - phasePos, - /* isInjector */ true, - this->summaryState_); + const Scalar local_current_rate = this->wgHelper().sumWellSurfaceRates( + group, phasePos, /*is_injector=*/true + ); // Sum over all processes rates[phasePos] = comm_.sum(local_current_rate) - reduction_rates[phasePos]; } @@ -698,22 +688,16 @@ checkGroupHigherConstraints(const Group& group, // Check higher up only if under individual (not FLD) control. if (currentControl != Group::InjectionCMode::FLD && group.injectionGroupControlAvailable(phase)) { const Group& parentGroup = schedule().getGroup(group.parent(), reportStepIdx); - const auto [is_changed, scaling_factor] = - WellGroupHelpersType::checkGroupConstraintsInj(group.name(), - group.parent(), - parentGroup, - this->wellState(), - this->groupState(), - reportStepIdx, - &guideRate_, - rates.data(), - phase, - group.getGroupEfficiencyFactor(), - schedule(), - summaryState_, - resv_coeff_inj, - /*check_guide_rate*/true, - deferred_logger); + const auto [is_changed, scaling_factor] = this->wgHelper().checkGroupConstraintsInj( + group.name(), + group.parent(), + parentGroup, + rates.data(), + phase, + group.getGroupEfficiencyFactor(), + resv_coeff_inj, + /*check_guide_rate*/true + ); if (is_changed) { auto& group_log = switched_inj_groups_[group.name()][static_cast>(phase)]; if (update_group_switching_log || group_log.empty()) { @@ -723,13 +707,9 @@ checkGroupHigherConstraints(const Group& group, actionOnBrokenConstraints(group, Group::InjectionCMode::FLD, phase, this->groupState(), deferred_logger); - WellGroupHelpersType::updateWellRatesFromGroupTargetScale(scaling_factor, - group, - schedule(), - reportStepIdx, - /* isInjector */ true, - this->groupState(), - this->wellState()); + this->wgHelper().updateWellRatesFromGroupTargetScale( + scaling_factor, group, /*is_injector=*/true + ); changed = true; } } @@ -762,13 +742,9 @@ checkGroupHigherConstraints(const Group& group, } } for (int phasePos = 0; phasePos < this->numPhases(); ++phasePos) { - const Scalar local_current_rate = WellGroupHelpersType::sumWellSurfaceRates(group, - schedule(), - this->wellState(), - reportStepIdx, - phasePos, - /* isInjector */ false, - this->summaryState_); + const Scalar local_current_rate = this->wgHelper().sumWellSurfaceRates( + group, phasePos, /*is_injector=*/false + ); // Sum over all processes rates[phasePos] = -comm_.sum(local_current_rate) - reduction_rates[phasePos]; } @@ -777,28 +753,22 @@ checkGroupHigherConstraints(const Group& group, // Check higher up only if under individual (not FLD) control. if (currentControl != Group::ProductionCMode::FLD && group.productionGroupControlAvailable()) { const Group& parentGroup = schedule().getGroup(group.parent(), reportStepIdx); - const auto [is_changed, scaling_factor] = - WellGroupHelpersType::checkGroupConstraintsProd(group.name(), - group.parent(), - parentGroup, - this->wellState(), - this->groupState(), - reportStepIdx, - &guideRate_, - rates.data(), - group.getGroupEfficiencyFactor(), - schedule(), - summaryState_, - resv_coeff, - /*check_guide_rate*/true, - deferred_logger); + const auto [is_changed, scaling_factor] = this->wgHelper().checkGroupConstraintsProd( + group.name(), + group.parent(), + parentGroup, + rates.data(), + group.getGroupEfficiencyFactor(), + resv_coeff, + /*check_guide_rate*/true + ); if (is_changed) { const auto group_limit_action = group.productionControls(summaryState_).group_limit_action; std::optional worst_offending_well = std::nullopt; changed = BlackoilWellModelConstraints(*this). - actionOnBrokenConstraints(group, reportStepIdx, group_limit_action, + actionOnBrokenConstraints(group, + group_limit_action, Group::ProductionCMode::FLD, - this->wellState(), worst_offending_well, this->groupState(), deferred_logger); @@ -807,13 +777,9 @@ checkGroupHigherConstraints(const Group& group, if (update_group_switching_log || switched_prod_groups_[group.name()].empty()) { switched_prod_groups_[group.name()].push_back(currentControl); } - WellGroupHelpersType::updateWellRatesFromGroupTargetScale(scaling_factor, - group, - schedule(), - reportStepIdx, - /* isInjector */ false, - this->groupState(), - this->wellState()); + this->wgHelper().updateWellRatesFromGroupTargetScale( + scaling_factor, group, /*is_injector=*/false + ); } } } @@ -1006,18 +972,12 @@ updateWsolvent(const Group& group, const int gasPos = phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); const auto& controls = group.injectionControls(Phase::GAS, summaryState_); const Group& groupRein = schedule_.getGroup(controls.reinj_group, reportStepIdx); - Scalar gasProductionRate = WellGroupHelpersType::sumWellSurfaceRates(groupRein, - schedule_, - wellState, - reportStepIdx, - gasPos, - /*isInjector*/false, - this->summaryState_); - Scalar solventProductionRate = WellGroupHelpersType::sumSolventRates(groupRein, - schedule_, - wellState, - reportStepIdx, - /*isInjector*/false); + Scalar gasProductionRate = this->wgHelper().sumWellSurfaceRates( + groupRein, gasPos, /*is_injector=*/false + ); + Scalar solventProductionRate = this->wgHelper().sumSolventRates( + groupRein, /*is_injector=*/false + ); solventProductionRate = comm_.sum(solventProductionRate); gasProductionRate = comm_.sum(gasProductionRate); @@ -1260,13 +1220,11 @@ assignNodeValues(std::map& nodevalues, return; } - auto converged_pressures = WellGroupHelpersType::computeNetworkPressures(network, - this->wellState(), - this->groupState(), - *(vfp_properties_->getProd()), - schedule(), - comm_, - reportStepIdx); + auto converged_pressures = this->wgHelper().computeNetworkPressures( + network, + *(this->vfp_properties_->getProd()), + this->comm_ + ); for (const auto& [node, converged_pressure] : converged_pressures) { auto it = nodevalues.find(node); assert(it != nodevalues.end() ); @@ -1327,6 +1285,7 @@ updateAndCommunicateGroupData(const int reportStepIdx, this->wellState().updateGlobalIsGrup(comm_, well_status); + WellGroupHelperType &wg_helper = this->wgHelper(); if (iterationIdx < nupcol) { OPM_TIMEBLOCK(updateNupcol); this->updateNupcolWGState(); @@ -1342,26 +1301,25 @@ updateAndCommunicateGroupData(const int reportStepIdx, const Group& group = schedule().getGroup(gr_name, reportStepIdx); const int np = this->wellState().numPhases(); Scalar gr_rate_nupcol = 0.0; - for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { - gr_rate_nupcol += WellGroupHelpersType::sumWellPhaseRates(is_vrep, - group, - schedule(), - this->nupcolWellState(), - this->summaryState_, - reportStepIdx, - phaseIdx, - /*isInjector*/ false); + { + auto guard = wg_helper.pushWellState(this->nupcolWellState()); + for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { + gr_rate_nupcol += wg_helper.sumWellPhaseRates( + /*res_rates=*/is_vrep, + group, + phaseIdx, + /*is_injector=*/false + ); + } } Scalar gr_rate = 0.0; for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { - gr_rate += WellGroupHelpersType::sumWellPhaseRates(is_vrep, - group, - schedule(), - this->wellState(), - this->summaryState_, - reportStepIdx, - phaseIdx, - /*isInjector*/ false); + gr_rate += wg_helper.sumWellPhaseRates( + /*res_rates=*/is_vrep, + group, + phaseIdx, + /*is_injector=*/false + ); } Scalar small_rate = 1e-12; // m3/s Scalar denominator = (0.5*gr_rate_nupcol + 0.5*gr_rate); @@ -1379,93 +1337,29 @@ updateAndCommunicateGroupData(const int reportStepIdx, } } } - auto& well_state = this->wellState(); - const auto& well_state_nupcol = this->nupcolWellState(); - - constexpr int num_configs = 4; - constexpr std::array is_production_group = {true, false, false, false}; - constexpr std::array phases = { Phase::OIL, Phase::WATER, Phase::OIL, Phase::GAS }; - for (int i = 0; i < num_configs; i++) { - WellGroupHelpersType::updateGroupControlledWells(schedule(), - well_state, - this->groupState(), - summaryState_, - &guideRate_, - reportStepIdx, - "FIELD", - is_production_group[i], - phases[i]); + { + constexpr int num_configs = 4; + constexpr std::array is_production_group = {true, false, false, false}; + constexpr std::array phases = { Phase::OIL, Phase::WATER, Phase::OIL, Phase::GAS }; + for (int i = 0; i < num_configs; i++) { + wg_helper.updateGroupControlledWells(is_production_group[i], phases[i]); + } } - // the group target reduction rates needs to be update since wells may have switched to/from GRUP control // The group target reduction does not honor NUPCOL. - std::vector groupTargetReduction(numPhases(), 0.0); - WellGroupHelpersType::updateGroupTargetReduction(fieldGroup, - schedule(), - reportStepIdx, - /*isInjector*/ false, - guideRate_, - well_state, - summaryState_, - this->groupState(), - groupTargetReduction); - std::vector groupTargetReductionInj(numPhases(), 0.0); - WellGroupHelpersType::updateGroupTargetReduction(fieldGroup, - schedule(), - reportStepIdx, - /*isInjector*/ true, - guideRate_, - well_state, - summaryState_, - this->groupState(), - groupTargetReductionInj); - - WellGroupHelpersType::updateREINForGroups(fieldGroup, - schedule(), - reportStepIdx, - summaryState_, - well_state_nupcol, - this->groupState(), - comm_.rank() == 0); - WellGroupHelpersType::updateVREPForGroups(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState(), - this->summaryState_); - - WellGroupHelpersType::updateReservoirRatesInjectionGroups(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState(), - this->summaryState_); - WellGroupHelpersType::updateSurfaceRatesInjectionGroups(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState(), - this->summaryState_); - WellGroupHelpersType::updateNetworkLeafNodeProductionRates(schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState(), - this->summaryState_); - - WellGroupHelpersType::updateGroupProductionRates(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState(), - this->summaryState_); - - WellGroupHelpersType::updateWellRates(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - well_state); - - well_state.communicateGroupRates(comm_); + wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/false); + wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/true); + { + auto guard = wg_helper.pushWellState(this->nupcolWellState()); + wg_helper.updateREINForGroups(fieldGroup, /*sum_rank=*/comm_.rank() == 0); + wg_helper.updateVREPForGroups(fieldGroup); + wg_helper.updateReservoirRatesInjectionGroups(fieldGroup); + wg_helper.updateSurfaceRatesInjectionGroups(fieldGroup); + wg_helper.updateNetworkLeafNodeProductionRates(); + wg_helper.updateGroupProductionRates(fieldGroup); + } + wg_helper.updateWellRates(fieldGroup, this->nupcolWellState()); + this->wellState().communicateGroupRates(comm_); this->groupState().communicate_rates(comm_); if (update_wellgrouptarget) { @@ -1481,20 +1375,14 @@ updateAndCommunicateGroupData(const int reportStepIdx, // Translate injector type from control to Phase. Scalar group_target = std::numeric_limits::max(); if (well->isProducer()) { - group_target = - WellGroupHelpersType::getWellGroupTargetProducer(well->name(), - well->wellEcl().groupName(), - group, - this->wellState(), - this->groupState(), - well->currentStep(), - well->guideRate(), - ws.surface_rates.data(), - efficiencyFactor, - this->schedule(), - summaryState_, - resv_coeff, - deferred_logger); + group_target = wg_helper.getWellGroupTargetProducer( + well->name(), + well->wellEcl().groupName(), + group, + ws.surface_rates.data(), + efficiencyFactor, + resv_coeff + ); } else { const auto& well_controls = well->wellEcl().injectionControls(summaryState_); auto injectorType = well_controls.injector_type; @@ -1518,21 +1406,15 @@ updateAndCommunicateGroupData(const int reportStepIdx, default: throw std::logic_error("MULTI-phase injection is not supported, but was requested for well " + well->name()); } - group_target = - WellGroupHelpersType::getWellGroupTargetInjector(well->name(), - well->wellEcl().groupName(), - group, - this->wellState(), - this->groupState(), - well->currentStep(), - well->guideRate(), - ws.surface_rates.data(), - injectionPhase, - efficiencyFactor, - this->schedule(), - summaryState_, - resv_coeff, - deferred_logger); + group_target = wg_helper.getWellGroupTargetInjector( + well->name(), + well->wellEcl().groupName(), + group, + ws.surface_rates.data(), + injectionPhase, + efficiencyFactor, + resv_coeff + ); } auto& ws_update = this->wellState().well(well->indexOfWell()); ws_update.group_target = group_target; @@ -1669,13 +1551,8 @@ updateNetworkPressures(const int reportStepIdx, const Scalar damping_factor, con const auto previous_node_pressures = node_pressures_; - node_pressures_ = WellGroupHelpersType::computeNetworkPressures(network, - this->wellState(), - this->groupState(), - *(vfp_properties_->getProd()), - schedule(), - comm_, - reportStepIdx); + node_pressures_ = this->wgHelper().computeNetworkPressures( + network, *(vfp_properties_->getProd()), comm_); // here, the network imbalance is the difference between the previous nodal pressure and the new nodal pressure Scalar network_imbalance = 0.; @@ -1742,11 +1619,10 @@ calculateEfficiencyFactors(const int reportStepIdx) const Well& wellEcl = well->wellEcl(); Scalar well_efficiency_factor = wellEcl.getEfficiencyFactor() * wellState().getGlobalEfficiencyScalingFactor(well->name()); - WellGroupHelpersType::accumulateGroupEfficiencyFactor(schedule().getGroup(wellEcl.groupName(), - reportStepIdx), - schedule(), - reportStepIdx, - well_efficiency_factor); + this->wgHelper().accumulateGroupEfficiencyFactor( + schedule().getGroup(wellEcl.groupName(), reportStepIdx), + well_efficiency_factor + ); well->setWellEfficiencyFactor(well_efficiency_factor); } } diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index f6c8ac31c9..189fc82246 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,6 @@ namespace Opm { struct SimulatorUpdate; class SummaryConfig; template class VFPProperties; - template class WellGroupHelpers; template class WellInterfaceGeneric; template class WellState; } // namespace Opm @@ -93,7 +93,7 @@ namespace Opm { template class BlackoilWellModelGeneric { - using WellGroupHelpersType = WellGroupHelpers; + using WellGroupHelperType = WellGroupHelper; public: BlackoilWellModelGeneric(Schedule& schedule, BlackoilWellModelGasLiftGeneric& gaslift, @@ -170,6 +170,10 @@ public: { return this->nupcol_wgstate_.well_state; } + WellState& nupcolWellState() + { + return this->nupcol_wgstate_.well_state; + } GroupState& groupState() { return this->active_wgstate_.group_state; } WellTestState& wellTestState() { return this->active_wgstate_.well_test_state; } @@ -295,6 +299,9 @@ public: const ConnectionIndexMap& connectionIndexMap(const std::size_t idx) { return conn_idx_map_[idx]; } + WellGroupHelperType& wgHelper() { return wg_helper_; } + const WellGroupHelperType& wgHelper() const { return wg_helper_; } + protected: /* The dynamic state of the well model is maintained with an instance @@ -575,6 +582,7 @@ protected: WGState active_wgstate_; WGState last_valid_wgstate_; WGState nupcol_wgstate_; + WellGroupHelperType wg_helper_{}; WellGroupEvents report_step_start_events_; //!< Well group events at start of report step bool wellStructureChangedDynamically_{false}; diff --git a/opm/simulators/wells/BlackoilWellModelGuideRates.cpp b/opm/simulators/wells/BlackoilWellModelGuideRates.cpp index db63de4fd5..10f2d488ee 100644 --- a/opm/simulators/wells/BlackoilWellModelGuideRates.cpp +++ b/opm/simulators/wells/BlackoilWellModelGuideRates.cpp @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include @@ -331,8 +331,7 @@ getGuideRateValues(const Well& well) const return grval; } - const auto qs = WellGroupHelpers:: - getWellRateVector(wellModel_.wellState(), wname); + const auto qs = this->wgHelper().getWellRateVector(wname); this->getGuideRateValues(qs, well.isInjector(), wname, grval); @@ -359,8 +358,7 @@ getGuideRateValues(const Group& group) const return grval; } - const auto qs = WellGroupHelpers:: - getProductionGroupRateVector(wellModel_.groupState(), wellModel_.phaseUsage(), gname); + const auto qs = this->wgHelper().getProductionGroupRateVector(gname); const auto is_inj = false; // This procedure only applies to G*PGR. this->getGuideRateValues(qs, is_inj, gname, grval); @@ -439,8 +437,7 @@ calculateWellGuideRates(const int reportStepIdx) const const auto get_gr = parent || RetrieveWellGuideRate{wellModel_.guideRate(), wname}; - const auto qs = WellGroupHelpers:: - getWellRateVector(wellModel_.wellState(), wname); + const auto qs = this->wgHelper().getWellRateVector(wname); auto getGR = [this, &wname, &qs](const GuideRateModel::Target t) { diff --git a/opm/simulators/wells/BlackoilWellModelGuideRates.hpp b/opm/simulators/wells/BlackoilWellModelGuideRates.hpp index 86a7e8adec..6be3364137 100644 --- a/opm/simulators/wells/BlackoilWellModelGuideRates.hpp +++ b/opm/simulators/wells/BlackoilWellModelGuideRates.hpp @@ -30,6 +30,7 @@ namespace Opm { +template class WellGroupHelper; template class BlackoilWellModelGeneric; namespace data { struct GroupData; @@ -45,6 +46,8 @@ template class BlackoilWellModelGuideRates { public: + using WellGroupHelperType = WellGroupHelper; + //! \brief Constructor initializes reference to the well model. explicit BlackoilWellModelGuideRates(const BlackoilWellModelGeneric& wellModel) : wellModel_(wellModel) @@ -70,6 +73,8 @@ public: //! \brief Check if a guide rate update is needed. bool guideRateUpdateIsNeeded(const int reportStepIdx) const; + const WellGroupHelperType& wgHelper() const { return wellModel_.wgHelper(); } + private: //! \brief Obtain guide rate values. void getGuideRateValues(const GuideRate::RateVector& qs, diff --git a/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp b/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp index 2d04825d1c..748004c20e 100644 --- a/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp @@ -63,8 +63,7 @@ assembleWellEq(const double dt, if (this->well_domain().at(well->name()) == domain.index) { well->assembleWellEq(wellModel_.simulator(), dt, - wellModel_.wellState(), - wellModel_.groupState(), + wellModel_.wgHelper(), deferred_logger); } } @@ -192,8 +191,7 @@ updateWellControls(DeferredLogger& deferred_logger, constexpr auto mode = WellInterface::IndividualOrGroup::Individual; well->updateWellControl(wellModel_.simulator(), mode, - wellModel_.wellState(), - wellModel_.groupState(), + wellModel_.wgHelper(), deferred_logger); } } diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 0f97b65d28..5fdd6801e3 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include @@ -195,6 +195,8 @@ namespace Opm { { DeferredLogger local_deferredLogger{}; + this->wgHelper().updateState(this->wellState(), this->groupState(), timeStepIdx); + this->wgHelper().setLogger(&local_deferredLogger); this->report_step_starts_ = true; this->report_step_start_events_ = this->schedule()[timeStepIdx].wellgroup_events(); @@ -299,20 +301,16 @@ namespace Opm { const auto& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx); - WellGroupHelpersType::setCmodeGroup(fieldGroup, - this->schedule(), - this->summaryState(), - reportStepIdx, - this->groupState()); + this->wgHelper().setCmodeGroup(fieldGroup); // Define per region average pressure calculators for use by // pressure maintenance groups (GPMAINT keyword). if (this->schedule()[reportStepIdx].has_gpmaint()) { - WellGroupHelpersType::setRegionAveragePressureCalculator(fieldGroup, - this->schedule(), - reportStepIdx, - this->eclState_.fieldProps(), - this->regionalAveragePressureCalculator_); + this->wgHelper().setRegionAveragePressureCalculator( + fieldGroup, + this->eclState_.fieldProps(), + this->regionalAveragePressureCalculator_ + ); } } OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger, @@ -364,9 +362,9 @@ namespace Opm { } this->resetWGState(); - const int reportStepIdx = simulator_.episodeIndex(); - + this->wgHelper().updateState(this->wellState(), this->groupState(), reportStepIdx); + this->wgHelper().setLogger(&local_deferredLogger); this->wellState().updateWellsDefaultALQ(this->schedule(), reportStepIdx, this->summaryState()); this->wellState().gliftTimeStepInit(); @@ -482,13 +480,9 @@ namespace Opm { } const double dt = simulator_.timeStepSize(); const Group& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx); - WellGroupHelpers::updateGpMaintTargetForGroups(fieldGroup, - this->schedule_, - regionalAveragePressureCalculator_, - reportStepIdx, - dt, - this->wellState(), - this->groupState()); + this->wgHelper().updateGpMaintTargetForGroups(fieldGroup, + regionalAveragePressureCalculator_, + dt); } this->updateAndCommunicateGroupData(reportStepIdx, @@ -513,9 +507,9 @@ namespace Opm { try { well->scaleSegmentRatesAndPressure(this->wellState()); well->calculateExplicitQuantities(simulator_, this->wellState(), local_deferredLogger); - well->updateWellStateWithTarget(simulator_, this->groupState(), this->wellState(), local_deferredLogger); + well->updateWellStateWithTarget(simulator_, this->wgHelper(), local_deferredLogger); well->updatePrimaryVariables(simulator_, this->wellState(), local_deferredLogger); - well->solveWellEquation(simulator_, this->wellState(), this->groupState(), local_deferredLogger); + well->solveWellEquation(simulator_, this->wgHelper(), local_deferredLogger); } catch (const std::exception& e) { const std::string msg = "Compute initial well solution for new well " + well->name() + " failed. Continue with zero initial rates"; local_deferredLogger.warning("WELL_INITIAL_SOLVE_FAILED", msg); @@ -554,11 +548,10 @@ namespace Opm { Scalar well_efficiency_factor = wellEcl.getEfficiencyFactor() * this->wellState().getGlobalEfficiencyScalingFactor(well_name); - WellGroupHelpersType::accumulateGroupEfficiencyFactor(this->schedule().getGroup(wellEcl.groupName(), - timeStepIdx), - this->schedule(), - timeStepIdx, - well_efficiency_factor); + this->wgHelper().accumulateGroupEfficiencyFactor( + this->schedule().getGroup(wellEcl.groupName(), timeStepIdx), + well_efficiency_factor + ); well->setWellEfficiencyFactor(well_efficiency_factor); well->setVFPProperties(this->vfp_properties_.get()); @@ -590,8 +583,7 @@ namespace Opm { gaslift_.initGliftEclWellMap(well_container_, ecl_well_map); well->wellTesting(simulator_, simulationTime, - this->wellState(), - this->groupState(), + this->wgHelper(), this->wellTestState(), ecl_well_map, this->well_open_times_, @@ -1140,6 +1132,7 @@ namespace Opm { OPM_TIMEFUNCTION(); DeferredLogger local_deferredLogger; + this->wgHelper().setLogger(&local_deferredLogger); this->guide_rate_handler_.setLogger(&local_deferredLogger); if constexpr (BlackoilWellModelGasLift::glift_debug) { if (gaslift_.terminalOutput()) { @@ -1366,8 +1359,7 @@ namespace Opm { auto target = WellGroupControls::getAutoChokeGroupProductionTargetRate( group.name(), parentGroup, - well_state, - group_state, + this->wgHelper(), this->schedule(), summary_state, resv_coeff, @@ -1402,7 +1394,7 @@ namespace Opm { const Well& well_ecl = this->wells_ecl_[well->indexOfWell()]; const auto inj_controls = Well::InjectionControls(0); const auto prod_controls = well_ecl.productionControls(summary_state); - well->iterateWellEqWithSwitching(this->simulator_, dt, inj_controls, prod_controls, well_state, group_state, local_deferredLogger, false, false); + well->iterateWellEqWithSwitching(this->simulator_, dt, inj_controls, prod_controls, this->wgHelper(), local_deferredLogger, false, false); rate = -tcalc.calcModeRateFromRates(ws.surface_rates); group_rate += rate; } @@ -1490,7 +1482,7 @@ namespace Opm { const auto& ws = this->wellState().well(well->indexOfWell()); const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP; if (thp_is_limit) { - well->prepareWellBeforeAssembling(this->simulator_, dt, this->wellState(), this->groupState(), local_deferredLogger); + well->prepareWellBeforeAssembling(this->simulator_, dt, this->wgHelper(), local_deferredLogger); } } @@ -1512,7 +1504,7 @@ namespace Opm { { OPM_TIMEFUNCTION(); for (auto& well : well_container_) { - well->assembleWellEq(simulator_, dt, this->wellState(), this->groupState(), deferred_logger); + well->assembleWellEq(simulator_, dt, this->wgHelper(), deferred_logger); } } @@ -1524,7 +1516,7 @@ namespace Opm { { OPM_TIMEFUNCTION(); for (auto& well : well_container_) { - well->prepareWellBeforeAssembling(simulator_, dt, this->wellState(), this->groupState(), deferred_logger); + well->prepareWellBeforeAssembling(simulator_, dt, this->wgHelper(), deferred_logger); } } @@ -1806,7 +1798,7 @@ namespace Opm { OPM_BEGIN_PARALLEL_TRY_CATCH() for (const auto& well : well_container_) { const auto mode = WellInterface::IndividualOrGroup::Group; - const bool changed_well = well->updateWellControl(simulator_, mode, this->wellState(), this->groupState(), deferred_logger); + const bool changed_well = well->updateWellControl(simulator_, mode, this->wgHelper(), deferred_logger); if (changed_well) { changed_well_to_group = changed_well || changed_well_to_group; } @@ -1829,7 +1821,7 @@ namespace Opm { OPM_BEGIN_PARALLEL_TRY_CATCH() for (const auto& well : well_container_) { const auto mode = WellInterface::IndividualOrGroup::Individual; - const bool changed_well = well->updateWellControl(simulator_, mode, this->wellState(), this->groupState(), deferred_logger); + const bool changed_well = well->updateWellControl(simulator_, mode, this->wgHelper(), deferred_logger); if (changed_well) { changed_well_individual = changed_well || changed_well_individual; } @@ -1901,7 +1893,7 @@ namespace Opm { const auto& ws = this->wellState().well(well->indexOfWell()); const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP; if (thp_is_limit) { - well->prepareWellBeforeAssembling(this->simulator_, dt, this->wellState(), this->groupState(), deferred_logger); + well->prepareWellBeforeAssembling(this->simulator_, dt, this->wgHelper(), deferred_logger); } } } @@ -1937,8 +1929,7 @@ namespace Opm { if (ws.production_cmode == Well::ProducerCMode::GRUP || ws.injection_cmode == Well::InjectorCMode::GRUP) { - well->updateWellStateWithTarget(simulator_, this->groupState(), - this->wellState(), deferred_logger); + well->updateWellStateWithTarget(simulator_, this->wgHelper(), deferred_logger); } } OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAndCommunicate failed: ", @@ -1980,7 +1971,6 @@ namespace Opm { this->switched_prod_groups_, this->closed_offending_wells_, this->groupState(), - this->wellState(), deferred_logger); if (changed_individual) { @@ -2007,6 +1997,7 @@ namespace Opm { const auto wasClosed = wellTestState.well_is_closed(wname); well->checkWellOperability(simulator_, this->wellState(), + this->wgHelper(), local_deferredLogger); const bool under_zero_target = well->wellUnderZeroGroupRateTarget(this->simulator_, @@ -2091,7 +2082,7 @@ namespace Opm { std::string cur_exc_msg; auto cur_exc_type = ExceptionType::NONE; try { - well->computeWellPotentials(simulator_, well_state_copy, potentials, deferred_logger); + well->computeWellPotentials(simulator_, well_state_copy, this->wgHelper(), potentials, deferred_logger); } // catch all possible exception and store type and message. OPM_PARALLEL_CATCH_CLAUSE(cur_exc_type, cur_exc_msg); @@ -2185,7 +2176,7 @@ namespace Opm { for (const auto& well : well_container_) { auto& events = this->wellState().well(well->indexOfWell()).events; if (events.hasEvent(WellState::event_mask)) { - well->updateWellStateWithTarget(simulator_, this->groupState(), this->wellState(), deferred_logger); + well->updateWellStateWithTarget(simulator_, this->wgHelper(), deferred_logger); well->updatePrimaryVariables(simulator_, this->wellState(), deferred_logger); // There is no new well control change input within a report step, // so next time step, the well does not consider to have effective events anymore. @@ -2198,7 +2189,7 @@ namespace Opm { // solve the well equation initially to improve the initial solution of the well model if (param_.solve_welleq_initially_ && well->isOperableAndSolvable()) { try { - well->solveWellEquation(simulator_, this->wellState(), this->groupState(), deferred_logger); + well->solveWellEquation(simulator_, this->wgHelper(), deferred_logger); } catch (const std::exception& e) { const std::string msg = "Compute initial well solution for " + well->name() + " initially failed. Continue with the previous rates"; deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg); diff --git a/opm/simulators/wells/FractionCalculator.cpp b/opm/simulators/wells/FractionCalculator.cpp index 0dcf358c64..c7050b96eb 100644 --- a/opm/simulators/wells/FractionCalculator.cpp +++ b/opm/simulators/wells/FractionCalculator.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -37,8 +38,7 @@ namespace Opm::WGHelpers { template FractionCalculator:: FractionCalculator(const Schedule& schedule, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const int report_step, const GuideRate* guide_rate, @@ -46,8 +46,7 @@ FractionCalculator(const Schedule& schedule, const bool is_producer, const Phase injection_phase) : schedule_(schedule) - , well_state_(well_state) - , group_state_(group_state) + , wgHelper_(wgHelper) , summary_state_(summary_state) , report_step_(report_step) , guide_rate_(guide_rate) @@ -129,11 +128,11 @@ guideRateSum(const Group& group, for (const std::string& child_group : group.groups()) { bool included = (child_group == always_included_child); if (is_producer_) { - const auto ctrl = this->group_state_.production_control(child_group); + const auto ctrl = this->wgHelper().groupState().production_control(child_group); included |= (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE); } else { - const auto ctrl = this->group_state_.injection_control(child_group, + const auto ctrl = this->wgHelper().groupState().injection_control(child_group, this->injection_phase_); included |= (ctrl == Group::InjectionCMode::FLD) || (ctrl == Group::InjectionCMode::NONE); @@ -149,9 +148,9 @@ guideRateSum(const Group& group, for (const std::string& child_well : group.wells()) { bool included = (child_well == always_included_child); if (is_producer_) { - included |= well_state_.isProductionGrup(child_well); + included |= this->wgHelper().wellState().isProductionGrup(child_well); } else { - included |= well_state_.isInjectionGrup(child_well); + included |= this->wgHelper().wellState().isInjectionGrup(child_well); } if (included) { number_of_included_well_or_groups++; @@ -169,8 +168,7 @@ guideRate(const std::string& name, const bool always_use_potentials) { if (schedule_.hasWell(name, report_step_)) { - return WellGroupHelpers::getGuideRate(name, schedule_, well_state_, group_state_, - report_step_, guide_rate_, target_); + return this->wgHelper().getGuideRate(name, target_); } else { if (groupControlledWells(name, always_included_child) > 0) { if (is_producer_ && guide_rate_->has(name) && !always_use_potentials) { @@ -196,14 +194,9 @@ int FractionCalculator:: groupControlledWells(const std::string& group_name, const std::string& always_included_child) { - return WellGroupHelpers::groupControlledWells(schedule_, - well_state_, - this->group_state_, - report_step_, - group_name, - always_included_child, - is_producer_, - injection_phase_); + return this->wgHelper().groupControlledWells( + group_name, always_included_child, is_producer_, injection_phase_ + ); } template @@ -211,9 +204,7 @@ GuideRate::RateVector FractionCalculator:: getGroupRateVector(const std::string& group_name) { assert(is_producer_); - return WellGroupHelpers::getProductionGroupRateVector(this->group_state_, - this->well_state_.phaseUsageInfo(), - group_name); + return this->wgHelper().getProductionGroupRateVector(group_name); } template class FractionCalculator; diff --git a/opm/simulators/wells/FractionCalculator.hpp b/opm/simulators/wells/FractionCalculator.hpp index 941dba81c2..49a06474b4 100644 --- a/opm/simulators/wells/FractionCalculator.hpp +++ b/opm/simulators/wells/FractionCalculator.hpp @@ -28,6 +28,7 @@ namespace Opm { template class GroupState; class Schedule; +template class WellGroupHelper; template class WellState; } @@ -37,9 +38,10 @@ template class FractionCalculator { public: + using WellGroupHelperType = WellGroupHelper; + FractionCalculator(const Schedule& schedule, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const int report_step, const GuideRate* guide_rate, @@ -66,9 +68,10 @@ private: int groupControlledWells(const std::string& group_name, const std::string& always_included_child); GuideRate::RateVector getGroupRateVector(const std::string& group_name); + const WellGroupHelperType& wgHelper() const { return wgHelper_; } + const Schedule& schedule_; - const WellState& well_state_; - const GroupState& group_state_; + const WellGroupHelperType& wgHelper_; const SummaryState& summary_state_; int report_step_; const GuideRate* guide_rate_; diff --git a/opm/simulators/wells/GasLiftSingleWell_impl.hpp b/opm/simulators/wells/GasLiftSingleWell_impl.hpp index 60aa414023..0c8f89f304 100644 --- a/opm/simulators/wells/GasLiftSingleWell_impl.hpp +++ b/opm/simulators/wells/GasLiftSingleWell_impl.hpp @@ -146,8 +146,10 @@ GasLiftSingleWell:: computeBhpAtThpLimit_(Scalar alq, bool debug_output) const { OPM_TIMEFUNCTION(); + const auto& wgHelper = this->simulator_.problem().wellModel().wgHelper(); auto bhp_at_thp_limit = this->well_.computeBhpAtThpLimitProdWithAlq( this->simulator_, + wgHelper, this->summary_state_, alq, this->deferred_logger_, diff --git a/opm/simulators/wells/MultisegmentWell.hpp b/opm/simulators/wells/MultisegmentWell.hpp index 122af65b8e..80ff953f16 100644 --- a/opm/simulators/wells/MultisegmentWell.hpp +++ b/opm/simulators/wells/MultisegmentWell.hpp @@ -52,6 +52,7 @@ namespace Opm { using typename Base::SparseMatrixAdapter; using typename Base::FluidState; using typename Base::WellStateType; + using typename Base::WellGroupHelperType; using Base::has_solvent; using Base::has_polymer; @@ -90,8 +91,7 @@ namespace Opm { /// updating the well state based the current control mode void updateWellStateWithTarget(const Simulator& simulator, - const GroupState& group_state, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const override; /// updating the segment pressure and rates based the current bhp and well rates @@ -119,6 +119,7 @@ namespace Opm { /// computing the well potentials for group control void computeWellPotentials(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) override; @@ -160,6 +161,7 @@ namespace Opm { std::optional computeBhpAtThpLimitProdWithAlq(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const Scalar alq_value, DeferredLogger& deferred_logger, @@ -234,6 +236,7 @@ namespace Opm { DeferredLogger& deferred_logger) const; void computeWellRatesAtBhpLimit(const Simulator& simulator, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const; @@ -244,16 +247,19 @@ namespace Opm { void computeWellRatesWithBhpIterations(const Simulator& simulator, const Scalar& bhp, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const override; std::vector computeWellPotentialWithTHP(const WellStateType& well_state, const Simulator& simulator, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const; bool computeWellPotentialsImplicit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const; @@ -263,16 +269,14 @@ namespace Opm { const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) override; bool iterateWellEqWithSwitching(const Simulator& simulator, const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false) override; @@ -305,11 +309,13 @@ namespace Opm { std::optional computeBhpAtThpLimitProd(const WellStateType& well_state, const Simulator& ebos_simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const; std::optional computeBhpAtThpLimitInj(const Simulator& ebos_simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const; @@ -323,6 +329,7 @@ namespace Opm { // check whether the well is operable under THP limit with current reservoir condition void checkOperabilityUnderTHPLimit(const Simulator& ebos_simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) override; // updating the inflow based on the current reservoir condition diff --git a/opm/simulators/wells/MultisegmentWell_impl.hpp b/opm/simulators/wells/MultisegmentWell_impl.hpp index d7c391adad..068fe63f6e 100644 --- a/opm/simulators/wells/MultisegmentWell_impl.hpp +++ b/opm/simulators/wells/MultisegmentWell_impl.hpp @@ -182,11 +182,11 @@ namespace Opm void MultisegmentWell:: updateWellStateWithTarget(const Simulator& simulator, - const GroupState& group_state, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const { - Base::updateWellStateWithTarget(simulator, group_state, well_state, deferred_logger); + auto& well_state = const_cast(wgHelper).wellState(); + Base::updateWellStateWithTarget(simulator, wgHelper, deferred_logger); // scale segment rates based on the wellRates // and segment pressure based on bhp this->scaleSegmentRatesWithWellRates(this->segments_.inlets(), @@ -296,6 +296,7 @@ namespace Opm MultisegmentWell:: computeWellPotentials(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) { @@ -309,7 +310,7 @@ namespace Opm debug_cost_counter_ = 0; bool converged_implicit = false; if (this->param_.local_well_solver_control_switching_) { - converged_implicit = computeWellPotentialsImplicit(simulator, well_state, well_potentials, deferred_logger); + converged_implicit = computeWellPotentialsImplicit(simulator, well_state, wgHelper, well_potentials, deferred_logger); if (!converged_implicit) { deferred_logger.debug("Implicit potential calculations failed for well " + this->name() + ", reverting to original aproach."); @@ -319,10 +320,10 @@ namespace Opm // does the well have a THP related constraint? const auto& summaryState = simulator.vanguard().summaryState(); if (!Base::wellHasTHPConstraints(summaryState) || bhp_controlled_well) { - computeWellRatesAtBhpLimit(simulator, well_potentials, deferred_logger); + computeWellRatesAtBhpLimit(simulator, wgHelper, well_potentials, deferred_logger); } else { well_potentials = computeWellPotentialWithTHP( - well_state, simulator, deferred_logger); + well_state, simulator, wgHelper, deferred_logger); } } deferred_logger.debug("Cost in iterations of finding well potential for well " @@ -340,15 +341,16 @@ namespace Opm void MultisegmentWell:: computeWellRatesAtBhpLimit(const Simulator& simulator, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const { if (this->well_ecl_.isInjector()) { const auto controls = this->well_ecl_.injectionControls(simulator.vanguard().summaryState()); - computeWellRatesWithBhpIterations(simulator, controls.bhp_limit, well_flux, deferred_logger); + computeWellRatesWithBhpIterations(simulator, controls.bhp_limit, wgHelper, well_flux, deferred_logger); } else { const auto controls = this->well_ecl_.productionControls(simulator.vanguard().summaryState()); - computeWellRatesWithBhpIterations(simulator, controls.bhp_limit, well_flux, deferred_logger); + computeWellRatesWithBhpIterations(simulator, controls.bhp_limit, wgHelper, well_flux, deferred_logger); } } @@ -404,6 +406,7 @@ namespace Opm MultisegmentWell:: computeWellRatesWithBhpIterations(const Simulator& simulator, const Scalar& bhp, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const { @@ -417,7 +420,7 @@ namespace Opm // store a copy of the well state, we don't want to update the real well state WellStateType well_state_copy = simulator.problem().wellModel().wellState(); - const auto& group_state = simulator.problem().wellModel().groupState(); + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); // Get the current controls. @@ -459,7 +462,7 @@ namespace Opm well_copy.calculateExplicitQuantities(simulator, well_state_copy, deferred_logger); const double dt = simulator.timeStepSize(); // iterate to get a solution at the given bhp. - well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, + well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); // compute the potential and store in the flux vector. @@ -479,6 +482,7 @@ namespace Opm MultisegmentWell:: computeWellPotentialWithTHP(const WellStateType& well_state, const Simulator& simulator, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const { std::vector potentials(this->number_of_phases_, 0.0); @@ -486,12 +490,12 @@ namespace Opm const auto& well = this->well_ecl_; if (well.isInjector()) { - auto bhp_at_thp_limit = computeBhpAtThpLimitInj(simulator, summary_state, deferred_logger); + auto bhp_at_thp_limit = computeBhpAtThpLimitInj(simulator, wgHelper, summary_state, deferred_logger); if (bhp_at_thp_limit) { const auto& controls = well.injectionControls(summary_state); const Scalar bhp = std::min(*bhp_at_thp_limit, static_cast(controls.bhp_limit)); - computeWellRatesWithBhpIterations(simulator, bhp, potentials, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, potentials, deferred_logger); deferred_logger.debug("Converged thp based potential calculation for well " + this->name() + ", at bhp = " + std::to_string(bhp)); } else { @@ -500,16 +504,16 @@ namespace Opm + this->name() + ". Instead the bhp based value is used"); const auto& controls = well.injectionControls(summary_state); const Scalar bhp = controls.bhp_limit; - computeWellRatesWithBhpIterations(simulator, bhp, potentials, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, potentials, deferred_logger); } } else { auto bhp_at_thp_limit = computeBhpAtThpLimitProd( - well_state, simulator, summary_state, deferred_logger); + well_state, simulator, wgHelper, summary_state, deferred_logger); if (bhp_at_thp_limit) { const auto& controls = well.productionControls(summary_state); const Scalar bhp = std::max(*bhp_at_thp_limit, static_cast(controls.bhp_limit)); - computeWellRatesWithBhpIterations(simulator, bhp, potentials, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, potentials, deferred_logger); deferred_logger.debug("Converged thp based potential calculation for well " + this->name() + ", at bhp = " + std::to_string(bhp)); } else { @@ -518,7 +522,7 @@ namespace Opm + this->name() + ". Instead the bhp based value is used"); const auto& controls = well.productionControls(summary_state); const Scalar bhp = controls.bhp_limit; - computeWellRatesWithBhpIterations(simulator, bhp, potentials, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, potentials, deferred_logger); } } @@ -530,6 +534,7 @@ namespace Opm MultisegmentWell:: computeWellPotentialsImplicit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const { @@ -541,7 +546,7 @@ namespace Opm // store a copy of the well state, we don't want to update the real well state WellStateType well_state_copy = well_state; - const auto& group_state = simulator.problem().wellModel().groupState(); + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); // get current controls @@ -579,9 +584,9 @@ namespace Opm // solve equations bool converged = false; if (this->well_ecl_.isProducer()) { - converged = well_copy.solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger); + converged = well_copy.solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } else { - converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger); + converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } // fetch potentials (sign is updated on the outside). @@ -1432,13 +1437,14 @@ namespace Opm MultisegmentWell:: checkOperabilityUnderTHPLimit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { const auto& summaryState = simulator.vanguard().summaryState(); const auto obtain_bhp = this->isProducer() ? computeBhpAtThpLimitProd( - well_state, simulator, summaryState, deferred_logger) - : computeBhpAtThpLimitInj(simulator, summaryState, deferred_logger); + well_state, simulator, wgHelper, summaryState, deferred_logger) + : computeBhpAtThpLimitInj(simulator, wgHelper, summaryState, deferred_logger); if (obtain_bhp) { this->operability_status_.can_obtain_bhp_with_thp_limit = true; @@ -1486,10 +1492,11 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { + auto& well_state = const_cast(wgHelper).wellState(); + const auto& group_state = wgHelper.groupState(); if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return true; const int max_iter_number = this->param_.max_inner_iter_ms_wells_; @@ -1610,12 +1617,13 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const bool fixed_control /*false*/, const bool fixed_status /*false*/) { + auto& well_state = const_cast(wgHelper).wellState(); + const auto& group_state = wgHelper.groupState(); const int max_iter_number = this->param_.max_inner_iter_ms_wells_; { @@ -1664,7 +1672,7 @@ namespace Opm ++its_since_last_switch; if (allow_switching && its_since_last_switch >= min_its_after_switch && status_switch_count < max_status_switch){ const Scalar wqTotal = this->primary_variables_.getWQTotal().value(); - bool changed = this->updateWellControlAndStatusLocalIteration(simulator, well_state, group_state, + bool changed = this->updateWellControlAndStatusLocalIteration(simulator, wgHelper, inj_controls, prod_controls, wqTotal, deferred_logger, fixed_control, fixed_status); @@ -2059,11 +2067,13 @@ namespace Opm MultisegmentWell:: computeBhpAtThpLimitProd(const WellStateType& well_state, const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const { return this->MultisegmentWell::computeBhpAtThpLimitProdWithAlq( simulator, + wgHelper, summary_state, this->getALQ(well_state), deferred_logger, @@ -2076,6 +2086,7 @@ namespace Opm std::optional::Scalar> MultisegmentWell:: computeBhpAtThpLimitProdWithAlq(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const Scalar alq_value, DeferredLogger& deferred_logger, @@ -2109,12 +2120,12 @@ namespace Opm if (!iterate_if_no_solution) return std::nullopt; - auto fratesIter = [this, &simulator, &deferred_logger](const Scalar bhp) { + auto fratesIter = [this, &simulator, &wgHelper, &deferred_logger](const Scalar bhp) { // Solver the well iterations to see if we are // able to get a solution with an update // solution std::vector rates(3); - computeWellRatesWithBhpIterations(simulator, bhp, rates, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, rates, deferred_logger); return rates; }; @@ -2132,6 +2143,7 @@ namespace Opm std::optional::Scalar> MultisegmentWell:: computeBhpAtThpLimitInj(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const { @@ -2159,12 +2171,12 @@ namespace Opm if (bhpAtLimit) return bhpAtLimit; - auto fratesIter = [this, &simulator, &deferred_logger](const Scalar bhp) { + auto fratesIter = [this, &simulator, &wgHelper, &deferred_logger](const Scalar bhp) { // Solver the well iterations to see if we are // able to get a solution with an update // solution std::vector rates(3); - computeWellRatesWithBhpIterations(simulator, bhp, rates, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, rates, deferred_logger); return rates; }; diff --git a/opm/simulators/wells/StandardWell.hpp b/opm/simulators/wells/StandardWell.hpp index c01efe54ce..827cb05a3e 100644 --- a/opm/simulators/wells/StandardWell.hpp +++ b/opm/simulators/wells/StandardWell.hpp @@ -77,6 +77,7 @@ namespace Opm using typename Base::SparseMatrixAdapter; using typename Base::FluidState; using typename Base::RateVector; + using typename Base::WellGroupHelperType; using Base::has_solvent; using Base::has_zFraction; @@ -161,6 +162,7 @@ namespace Opm /// computing the well potentials for group control void computeWellPotentials(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) /* const */ override; @@ -197,8 +199,7 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) override; // iterate well equations including control switching @@ -206,20 +207,21 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false) override; /* returns BHP */ Scalar computeWellRatesAndBhpWithThpAlqProd(const Simulator& ebos_simulator, + const WellGroupHelperType& wgHelper, const SummaryState &summary_state, DeferredLogger& deferred_logger, std::vector& potentials, Scalar alq) const; void computeWellRatesWithThpAlqProd(const Simulator& ebos_simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger, std::vector& potentials, @@ -227,6 +229,7 @@ namespace Opm std::optional computeBhpAtThpLimitProdWithAlq(const Simulator& ebos_simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const Scalar alq_value, DeferredLogger& deferred_logger, @@ -309,16 +312,19 @@ namespace Opm void computeWellRatesWithBhpIterations(const Simulator& ebosSimulator, const Scalar& bhp, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const override; std::vector computeWellPotentialWithTHP(const Simulator& ebosSimulator, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const WellStateType& well_state) const; bool computeWellPotentialsImplicit(const Simulator& ebos_simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const; @@ -379,6 +385,7 @@ namespace Opm // check whether the well is operable under THP limit with current reservoir condition void checkOperabilityUnderTHPLimit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) override; // updating the inflow based on the current reservoir condition @@ -445,11 +452,13 @@ namespace Opm std::optional computeBhpAtThpLimitProd(const WellStateType& well_state, const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const; std::optional computeBhpAtThpLimitInj(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const; diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index 724ad60722..41baca11f3 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -1036,11 +1036,12 @@ namespace Opm StandardWell:: checkOperabilityUnderTHPLimit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { const auto& summaryState = simulator.vanguard().summaryState(); - const auto obtain_bhp = this->isProducer() ? computeBhpAtThpLimitProd(well_state, simulator, summaryState, deferred_logger) - : computeBhpAtThpLimitInj(simulator, summaryState, deferred_logger); + const auto obtain_bhp = this->isProducer() ? computeBhpAtThpLimitProd(well_state, simulator, wgHelper, summaryState, deferred_logger) + : computeBhpAtThpLimitInj(simulator, wgHelper, summaryState, deferred_logger); if (obtain_bhp) { this->operability_status_.can_obtain_bhp_with_thp_limit = true; @@ -1516,6 +1517,7 @@ namespace Opm StandardWell:: computeWellRatesWithBhpIterations(const Simulator& simulator, const Scalar& bhp, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const { @@ -1528,7 +1530,7 @@ namespace Opm // create a copy of the well_state to use. If the operability checking is sucessful, we use this one // to replace the original one WellStateType well_state_copy = simulator.problem().wellModel().wellState(); - const auto& group_state = simulator.problem().wellModel().groupState(); + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); // Get the current controls. const auto& summary_state = simulator.vanguard().summaryState(); @@ -1561,7 +1563,7 @@ namespace Opm well_copy.computeAccumWell(); const double dt = simulator.timeStepSize(); - const bool converged = well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger); + const bool converged = well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); if (!converged) { const std::string msg = " well " + name() + " did not get converged during well potential calculations " " potentials are computed based on unconverged solution"; @@ -1579,6 +1581,7 @@ namespace Opm std::vector::Scalar> StandardWell:: computeWellPotentialWithTHP(const Simulator& simulator, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const WellStateType& well_state) const { @@ -1588,7 +1591,7 @@ namespace Opm const auto& well = this->well_ecl_; if (well.isInjector()){ const auto& controls = this->well_ecl_.injectionControls(summary_state); - auto bhp_at_thp_limit = computeBhpAtThpLimitInj(simulator, summary_state, deferred_logger); + auto bhp_at_thp_limit = computeBhpAtThpLimitInj(simulator, wgHelper, summary_state, deferred_logger); if (bhp_at_thp_limit) { const Scalar bhp = std::min(*bhp_at_thp_limit, static_cast(controls.bhp_limit)); @@ -1602,7 +1605,7 @@ namespace Opm } } else { computeWellRatesWithThpAlqProd( - simulator, summary_state, + simulator, wgHelper, summary_state, deferred_logger, potentials, this->getALQ(well_state) ); } @@ -1615,6 +1618,7 @@ namespace Opm StandardWell:: computeWellPotentialsImplicit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const { @@ -1625,7 +1629,7 @@ namespace Opm // store a copy of the well state, we don't want to update the real well state WellStateType well_state_copy = well_state; - const auto& group_state = simulator.problem().wellModel().groupState(); + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); // get current controls @@ -1663,9 +1667,9 @@ namespace Opm // iterate to get a solution at the given bhp. bool converged = false; if (this->well_ecl_.isProducer()) { - converged = well_copy.solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger); + converged = well_copy.solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } else { - converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger); + converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } // fetch potentials (sign is updated on the outside). @@ -1693,6 +1697,7 @@ namespace Opm typename StandardWell::Scalar StandardWell:: computeWellRatesAndBhpWithThpAlqProd(const Simulator &simulator, + const WellGroupHelperType& wgHelper, const SummaryState &summary_state, DeferredLogger& deferred_logger, std::vector& potentials, @@ -1700,7 +1705,7 @@ namespace Opm { Scalar bhp; auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq( - simulator, summary_state, alq, deferred_logger, /*iterate_if_no_solution */ true); + simulator, wgHelper, summary_state, alq, deferred_logger, /*iterate_if_no_solution */ true); if (bhp_at_thp_limit) { const auto& controls = this->well_ecl_.productionControls(summary_state); bhp = std::max(*bhp_at_thp_limit, @@ -1722,6 +1727,7 @@ namespace Opm void StandardWell:: computeWellRatesWithThpAlqProd(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger, std::vector& potentials, @@ -1729,6 +1735,7 @@ namespace Opm { /*double bhp =*/ computeWellRatesAndBhpWithThpAlqProd(simulator, + wgHelper, summary_state, deferred_logger, potentials, @@ -1740,6 +1747,7 @@ namespace Opm StandardWell:: computeWellPotentials(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) // const { @@ -1755,7 +1763,7 @@ namespace Opm // group controlled wells with defaulted guiderates will have zero targets as // the potentials are used to compute the well fractions. if (this->param_.local_well_solver_control_switching_ && !(this->changed_to_open_this_step_ && this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger))) { - converged_implicit = computeWellPotentialsImplicit(simulator, well_state, well_potentials, deferred_logger); + converged_implicit = computeWellPotentialsImplicit(simulator, well_state, wgHelper, well_potentials, deferred_logger); } if (!converged_implicit) { // does the well have a THP related constraint? @@ -1777,10 +1785,10 @@ namespace Opm bhp = std::min(ws.bhp, bhp); assert(std::abs(bhp) != std::numeric_limits::max()); - computeWellRatesWithBhpIterations(simulator, bhp, well_potentials, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, well_potentials, deferred_logger); } else { // the well has a THP related constraint - well_potentials = computeWellPotentialWithTHP(simulator, deferred_logger, well_state); + well_potentials = computeWellPotentialWithTHP(simulator, wgHelper, deferred_logger, well_state); } } @@ -2210,10 +2218,12 @@ namespace Opm StandardWell:: computeBhpAtThpLimitProd(const WellStateType& well_state, const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const { return computeBhpAtThpLimitProdWithAlq(simulator, + wgHelper, summary_state, this->getALQ(well_state), deferred_logger, @@ -2224,6 +2234,7 @@ namespace Opm std::optional::Scalar> StandardWell:: computeBhpAtThpLimitProdWithAlq(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const Scalar alq_value, DeferredLogger& deferred_logger, @@ -2273,12 +2284,12 @@ namespace Opm if (!iterate_if_no_solution) return std::nullopt; - auto fratesIter = [this, &simulator, &deferred_logger](const Scalar bhp) { + auto fratesIter = [this, &simulator, &wgHelper, &deferred_logger](const Scalar bhp) { // Solver the well iterations to see if we are // able to get a solution with an update // solution std::vector rates(3); - computeWellRatesWithBhpIterations(simulator, bhp, rates, deferred_logger); + computeWellRatesWithBhpIterations(simulator, bhp, wgHelper, rates, deferred_logger); this->adaptRatesForVFP(rates); return rates; }; @@ -2310,9 +2321,16 @@ namespace Opm std::optional::Scalar> StandardWell:: computeBhpAtThpLimitInj(const Simulator& simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const { + // Note: wgHelper parameter is currently unused in StandardWell but kept for consistency + // with MultisegmentWell::computeBhpAtThpLimitInj which uses it in fratesIter lambda. + // This maintains parallel API structure between well types and allows for future + // enhancements without breaking the interface. + static_cast(wgHelper); + // Make the frates() function. auto frates = [this, &simulator, &deferred_logger](const Scalar bhp) { // Not solving the well equations here, which means we are @@ -2345,10 +2363,11 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { + auto& well_state = const_cast(wgHelper).wellState(); + const auto& group_state = wgHelper.groupState(); updatePrimaryVariables(simulator, well_state, deferred_logger); const int max_iter = this->param_.max_inner_iter_wells_; @@ -2392,12 +2411,13 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const bool fixed_control /*false*/, const bool fixed_status /*false*/) { + auto& well_state = const_cast(wgHelper).wellState(); + const auto& group_state = wgHelper.groupState(); updatePrimaryVariables(simulator, well_state, deferred_logger); const int max_iter = this->param_.max_inner_iter_wells_; @@ -2436,7 +2456,7 @@ namespace Opm its_since_last_switch++; if (allow_switching && its_since_last_switch >= min_its_after_switch && status_switch_count < max_status_switch){ const Scalar wqTotal = this->primary_variables_.eval(WQTotal).value(); - changed = this->updateWellControlAndStatusLocalIteration(simulator, well_state, group_state, + changed = this->updateWellControlAndStatusLocalIteration(simulator, wgHelper, inj_controls, prod_controls, wqTotal, deferred_logger, fixed_control, fixed_status); if (changed){ diff --git a/opm/simulators/wells/WellGroupConstraints.cpp b/opm/simulators/wells/WellGroupConstraints.cpp index e823f8dd3c..f360eb4c2a 100644 --- a/opm/simulators/wells/WellGroupConstraints.cpp +++ b/opm/simulators/wells/WellGroupConstraints.cpp @@ -27,7 +27,7 @@ #include -#include +#include #include #include @@ -38,8 +38,7 @@ template std::pair WellGroupConstraints:: checkGroupConstraintsInj(const Group& group, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, const Schedule& schedule, const SummaryState& summaryState, @@ -47,6 +46,8 @@ checkGroupConstraintsInj(const Group& group, const bool check_guide_rate, DeferredLogger& deferred_logger) const { + auto& well_state = const_cast(wgHelper).wellState(); + // Translate injector type from control to Phase. const auto& well_controls = well_.wellEcl().injectionControls(summaryState); auto injectorType = well_controls.injector_type; @@ -77,67 +78,54 @@ checkGroupConstraintsInj(const Group& group, const auto& ws = well_state.well(well_.indexOfWell()); // Call check for the well's injection phase. - return WellGroupHelpers::checkGroupConstraintsInj(well_.name(), - well_.wellEcl().groupName(), - group, - well_state, - group_state, - well_.currentStep(), - well_.guideRate(), - ws.surface_rates.data(), - injectionPhase, - efficiencyFactor, - schedule, - summaryState, - resv_coeff, - check_guide_rate, - deferred_logger); + return wgHelper.checkGroupConstraintsInj( + well_.name(), + well_.wellEcl().groupName(), + group, + ws.surface_rates.data(), + injectionPhase, + efficiencyFactor, + resv_coeff, + check_guide_rate + ); } template std::pair WellGroupConstraints:: checkGroupConstraintsProd(const Group& group, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const + const bool check_guide_rate) const { + auto& well_state = const_cast(wgHelper).wellState(); + // Make conversion factors for RESV <-> surface rates. std::vector resv_coeff(well_.phaseUsage().numActivePhases(), 1.0); rateConverter(0, well_.pvtRegionIdx(), group.name(), resv_coeff); // FIPNUM region 0 here, should use FIPNUM from WELSPECS. const auto& ws = well_state.well(well_.indexOfWell()); - return WellGroupHelpers::checkGroupConstraintsProd(well_.name(), - well_.wellEcl().groupName(), - group, - well_state, - group_state, - well_.currentStep(), - well_.guideRate(), - ws.surface_rates.data(), - efficiencyFactor, - schedule, - summaryState, - resv_coeff, - check_guide_rate, - deferred_logger); + return wgHelper.checkGroupConstraintsProd(well_.name(), + well_.wellEcl().groupName(), + group, + ws.surface_rates.data(), + efficiencyFactor, + resv_coeff, + check_guide_rate + ); } template bool WellGroupConstraints:: -checkGroupConstraints(WellState& well_state, - const GroupState& group_state, +checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, const bool check_guide_rate, DeferredLogger& deferred_logger) const { + auto& well_state = const_cast(wgHelper).wellState(); const auto& well = well_.wellEcl(); const int well_index = well_.indexOfWell(); auto& ws = well_state.well(well_index); @@ -156,8 +144,7 @@ checkGroupConstraints(WellState& well_state, const Scalar efficiencyFactor = well.getEfficiencyFactor() * well_state[well.name()].efficiency_scaling_factor; const std::pair group_constraint = - this->checkGroupConstraintsInj(group, well_state, - group_state, efficiencyFactor, + this->checkGroupConstraintsInj(group, wgHelper, efficiencyFactor, schedule, summaryState, rateConverter, check_guide_rate, @@ -189,10 +176,9 @@ checkGroupConstraints(WellState& well_state, const Scalar efficiencyFactor = well.getEfficiencyFactor() * well_state[well.name()].efficiency_scaling_factor; const std::pair group_constraint = - this->checkGroupConstraintsProd(group, well_state, - group_state, efficiencyFactor, - schedule, summaryState, - rateConverter, check_guide_rate, deferred_logger); + this->checkGroupConstraintsProd(group, wgHelper, + efficiencyFactor, + rateConverter, check_guide_rate); // If a group constraint was broken, we set the current well control to // be GRUP. if (group_constraint.first) { diff --git a/opm/simulators/wells/WellGroupConstraints.hpp b/opm/simulators/wells/WellGroupConstraints.hpp index 5eaf171d78..c501739099 100644 --- a/opm/simulators/wells/WellGroupConstraints.hpp +++ b/opm/simulators/wells/WellGroupConstraints.hpp @@ -40,6 +40,7 @@ enum class InjectorType; using RegionId = int; class Schedule; class SummaryState; +template class WellGroupHelper; template class WellInterfaceGeneric; template class WellState; @@ -54,9 +55,9 @@ public: const int, const std::optional&, std::vector&)>; + using WellGroupHelperType = WellGroupHelper; - bool checkGroupConstraints(WellState& well_state, - const GroupState& group_state, + bool checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, @@ -66,8 +67,7 @@ public: private: std::pair checkGroupConstraintsInj(const Group& group, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, const Schedule& schedule, const SummaryState& summaryState, @@ -77,14 +77,10 @@ private: std::pair checkGroupConstraintsProd(const Group& group, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const; + const bool check_guide_rate) const; const WellInterfaceGeneric& well_; //!< Reference to well interface }; diff --git a/opm/simulators/wells/WellGroupControls.cpp b/opm/simulators/wells/WellGroupControls.cpp index 0d45a97806..c084ecaf01 100644 --- a/opm/simulators/wells/WellGroupControls.cpp +++ b/opm/simulators/wells/WellGroupControls.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -358,8 +358,7 @@ template std::pair WellGroupControls:: getAutoChokeGroupProductionTargetRate(const std::string& name, const Group& group, - const WellState& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const std::vector& resv_coeff, @@ -368,6 +367,8 @@ getAutoChokeGroupProductionTargetRate(const std::string& name, const GuideRate* guideRate, DeferredLogger& deferred_logger) { + const auto& group_state = wgHelper.groupState(); + const auto& well_state = wgHelper.wellState(); const Group::ProductionCMode& currentGroupControl = group_state.production_control(group.name()); if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) { @@ -377,7 +378,7 @@ getAutoChokeGroupProductionTargetRate(const std::string& name, // Produce share of parents control const auto& parent = schedule.getGroup(group.parent(), reportStepIdx); efficiencyFactor *= group.getGroupEfficiencyFactor(); - return getAutoChokeGroupProductionTargetRate(name, parent, well_state, group_state, + return getAutoChokeGroupProductionTargetRate(name, parent, wgHelper, schedule, summaryState, resv_coeff, efficiencyFactor, reportStepIdx, guideRate, deferred_logger); @@ -398,7 +399,7 @@ getAutoChokeGroupProductionTargetRate(const std::string& name, // gconsale may adjust the grat target. // the adjusted rates is send to the targetCalculator Scalar gratTargetFromSales = 0.0; - if (group_state.has_grat_sales_target(group.name())) + if (wgHelper.groupState().has_grat_sales_target(group.name())) gratTargetFromSales = group_state.grat_sales_target(group.name()); WGHelpers::TargetCalculator tcalc(currentGroupControl, @@ -410,8 +411,7 @@ getAutoChokeGroupProductionTargetRate(const std::string& name, group.has_gpmaint_control(currentGroupControl)); WGHelpers::FractionCalculator fcalc(schedule, - well_state, - group_state, + wgHelper, summaryState, reportStepIdx, guideRate, @@ -433,8 +433,7 @@ getAutoChokeGroupProductionTargetRate(const std::string& name, ctrl = group.productionControls(summaryState); const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); - const auto chain = WellGroupHelpers::groupChainTopBot(name, group.name(), - schedule, reportStepIdx); + const auto chain = wgHelper.groupChainTopBot(name, group.name()); // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. const std::size_t num_ancestors = chain.size() - 1; Scalar target = orig_target; diff --git a/opm/simulators/wells/WellGroupControls.hpp b/opm/simulators/wells/WellGroupControls.hpp index 492830a8db..efb24541e8 100644 --- a/opm/simulators/wells/WellGroupControls.hpp +++ b/opm/simulators/wells/WellGroupControls.hpp @@ -40,6 +40,7 @@ enum class InjectorType; using RegionId = int; class Schedule; class SummaryState; +template class WellGroupHelper; template class WellInterfaceGeneric; template class WellState; @@ -52,6 +53,7 @@ public: using RateConvFunc = std::function&, std::vector&)>; + using WellGroupHelperType = WellGroupHelper; template void getGroupInjectionControl(const Group& group, @@ -101,9 +103,8 @@ public: DeferredLogger& deferred_logger) const; static std::pair getAutoChokeGroupProductionTargetRate(const std::string& name, - const Group& parent, - const WellState& well_state, - const GroupState& group_state, + const Group& group, + const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const std::vector& resv_coeff, diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp new file mode 100644 index 0000000000..24bffa464d --- /dev/null +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -0,0 +1,1992 @@ +/* + Copyright 2025 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Opm { + +template +WellGroupHelper::WellGroupHelper( + const Schedule& schedule, + const SummaryState& summary_state, + const GuideRate& guide_rate, + const PhaseUsageInfo& phase_usage_info + +) : schedule_{schedule} + , summary_state_{summary_state} + , guide_rate_{guide_rate} + , phase_usage_info_{phase_usage_info} +{ +} + + +// --------------------------------------------------------------------- +// Public methods +// --------------------------------------------------------------------- + +template +void WellGroupHelper:: +accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) +{ + factor *= group.getGroupEfficiencyFactor(); + if (group.parent() != "FIELD" && !group.parent().empty()) + this->accumulateGroupEfficiencyFactor( + this->schedule_.getGroup(group.parent(), this->report_step_), factor); +} + +template +std::pair +WellGroupHelper:: +checkGroupConstraintsInj(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate) const +{ + // When called for a well ('name' is a well name), 'parent' + // will be the name of 'group'. But if we recurse, 'name' and + // 'parent' will stay fixed while 'group' will be higher up + // in the group tree. + // efficiencyfactor is the well efficiency factor for the first group the well is + // part of. Later it is the accumulated factor including the group efficiency factor + // of the child of group. + + // NOTE: if name is a group, the rates-argument is the portion of the 'full' group rates that + // is potentially available for control by an ancestor, i.e. full rates minus reduction rates + + auto current_group_control = this->groupState().injection_control(group.name(), injection_phase); + + if (current_group_control == Group::InjectionCMode::FLD || + current_group_control == Group::InjectionCMode::NONE) + { + // Return if we are not available for parent group. + if (!group.injectionGroupControlAvailable(injection_phase)) { + return std::make_pair(false, Scalar(1)); + } + // Otherwise: check production share of parent's control. + const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); + return this->checkGroupConstraintsInj( + name, + parent, + parent_group, + rates, + injection_phase, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff, + check_guide_rate + ); + } + + // This can be false for FLD-controlled groups, we must therefore + // check for FLD first (done above). + if (!group.isInjectionGroup()) { + return std::make_pair(false, Scalar(1.0)); + } + + // If we are here, we are at the topmost group to be visited in the recursion. + // This is the group containing the control we will check against. + Scalar sales_target = 0; + if (this->schedule_[this->report_step_].gconsale().has(group.name())) { + const auto& gconsale = this->schedule_[this->report_step_].gconsale().get(group.name(), this->summary_state_); + sales_target = gconsale.sales_target; + } + WGHelpers::InjectionTargetCalculator tcalc{ + current_group_control, + this->phase_usage_info_, + resv_coeff, + group.name(), + sales_target, + this->groupState(), + injection_phase, + group.has_gpmaint_control(injection_phase, current_group_control), + this->deferredLogger() + }; + + WGHelpers::FractionCalculator fcalc{ + this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + /*is_production_group=*/false, + injection_phase + }; + + auto local_fraction_lambda = [&](const std::string& child) { return fcalc.localFraction(child, name); }; + + auto local_reduction_lambda = [&](const std::string& group_name) + { + const std::vector& group_target_reductions = + this->groupState().injection_reduction_rates(group_name); + return tcalc.calcModeRateFromRates(group_target_reductions); + }; + + auto local_current_rate_lambda = [&](const std::string& group_name) + { + const std::vector& group_surface_rates = + this->groupState().injection_surface_rates(group_name); + return tcalc.calcModeRateFromRates(group_surface_rates); + }; + + const auto chain = this->groupChainTopBot(name, group.name()); + // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. + const std::size_t num_ancestors = chain.size() - 1; + // we need to find out the level where the current well is applied to the local reduction + std::size_t local_reduction_level = 0; + for (std::size_t ii = 1; ii < num_ancestors; ++ii) { + const int num_gr_ctrl = this->groupControlledWells( + chain[ii], + /*always_included_child=*/"", + /*is_production_group=*/false, + injection_phase + ); + if (this->guide_rate_.has(chain[ii], injection_phase) && num_gr_ctrl > 0) { + local_reduction_level = ii; + } + } + + // check whether guide rate is violated + if (check_guide_rate) { + for (std::size_t ii = 1; ii < num_ancestors; ++ii) { + if (this->guide_rate_.has(chain[ii], injection_phase)) { + const auto& guided_group = chain[ii]; + const Scalar grefficiency = + this->schedule_.getGroup(guided_group, this->report_step_).getGroupEfficiencyFactor(); + const Scalar current_rate_fraction = grefficiency * local_current_rate_lambda(guided_group) / + local_current_rate_lambda(chain[ii-1]); + const Scalar guiderate_fraction = local_fraction_lambda(guided_group); + // we add a factor here to avoid switching due to numerical instability + const Scalar factor = 1.01; + if (current_rate_fraction > (guiderate_fraction * factor)) { + return std::make_pair(true, guiderate_fraction / current_rate_fraction); + } + } + } + } + + if (this->schedule_.hasWell(name) && this->wellState().well(name).group_target) { // for wells we already have computed the target + Scalar scale = 1.0; + const Scalar group_target_rate_available = *this->wellState().well(name).group_target; + const Scalar current_well_rate_available = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. + if (current_well_rate_available > 1e-12) { + scale = group_target_rate_available / current_well_rate_available; + } + return std::make_pair(current_well_rate_available > group_target_rate_available, scale); + } + + std::optional ctrl; + if (!group.has_gpmaint_control(injection_phase, current_group_control)) + ctrl = group.injectionControls(injection_phase, this->summary_state_); + + + const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. + // Then ... + // TODO finish explanation. + const Scalar current_rate_available + = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. + + // Compute portion of target corresponding to current_rate_available + Scalar target = orig_target; + for (std::size_t ii = 0; ii < num_ancestors; ++ii) { + if ((ii == 0) || this->guide_rate_.has(chain[ii], injection_phase)) { + // Apply local reductions only at the control level + // (top) and for levels where we have a specified + // group guide rate. + if (local_reduction_level >= ii ) { + target -= local_reduction_lambda(chain[ii]); + } + + // Add my reduction back at the level where it is included in the local reduction + if (local_reduction_level == ii ) { + target += current_rate_available * efficiency_factor; + } + } + target *= local_fraction_lambda(chain[ii + 1]); + } + // Avoid negative target rates comming from too large local reductions. + const Scalar target_rate_available = std::max(Scalar(1e-12), target / efficiency_factor); + Scalar scale = 1.0; + if (current_rate_available > 1e-12) + scale = target_rate_available / current_rate_available; + + return std::make_pair(current_rate_available > target_rate_available, scale); +} + +template +std::pair +WellGroupHelper:: +checkGroupConstraintsProd(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate) const +{ + // When called for a well ('name' is a well name), 'parent' + // will be the name of 'group'. But if we recurse, 'name' and + // 'parent' will stay fixed while 'group' will be higher up + // in the group tree. + // efficiencyfactor is the well efficiency factor for the first group the well is + // part of. Later it is the accumulated factor including the group efficiency factor + // of the child of group. + + // NOTE: if name is a group, the rates-argument is the portion of the 'full' group rates that + // is potentially available for control by an ancestor, i.e. full rates minus reduction rates + OPM_TIMEFUNCTION(); + const Group::ProductionCMode& current_group_control = this->groupState().production_control(group.name()); + + if (current_group_control == Group::ProductionCMode::FLD || current_group_control == Group::ProductionCMode::NONE) { + // Return if we are not available for parent group. + if (!group.productionGroupControlAvailable()) { + return std::make_pair(false, 1); + } + // Otherwise: check production share of parent's control. + const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); + return this->checkGroupConstraintsProd(name, + parent, + parent_group, + rates, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff, + check_guide_rate); + } + + // This can be false for FLD-controlled groups, we must therefore + // check for FLD first (done above). + if (!group.isProductionGroup()) { + return std::make_pair(false, Scalar(1.0)); + } + + // If we are here, we are at the topmost group to be visited in the recursion. + // This is the group containing the control we will check against. + + // gconsale may adjust the grat target. + // the adjusted rates is send to the targetCalculator + Scalar grat_target_from_sales = 0.0; + if (this->groupState().has_grat_sales_target(group.name())) + grat_target_from_sales = this->groupState().grat_sales_target(group.name()); + + WGHelpers::TargetCalculator tcalc{ + current_group_control, + this->phase_usage_info_, + resv_coeff, + grat_target_from_sales, + group.name(), + this->groupState(), + group.has_gpmaint_control(current_group_control) + }; + + WGHelpers::FractionCalculator fcalc{ + this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + /*is_production_group=*/true, + /*injection_phase=*/Phase::OIL + }; + + auto local_fraction_lambda = [&](const std::string& child) { return fcalc.localFraction(child, name); }; + + auto local_reduction_lambda = [&](const std::string& group_name) + { + const std::vector& group_target_reductions = + this->groupState().production_reduction_rates(group_name); + return tcalc.calcModeRateFromRates(group_target_reductions); + }; + + auto local_current_rate_lambda = [&](const std::string& group_name) + { + const std::vector& group_surface_rates = + this->groupState().production_rates(group_name); + return tcalc.calcModeRateFromRates(group_surface_rates); + }; + + std::optional ctrl; + if (!group.has_gpmaint_control(current_group_control)) + ctrl = group.productionControls(this->summary_state_); + + const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. + // Then ... + // TODO finish explanation. + const Scalar current_rate_available + = -tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. + const auto chain = this->groupChainTopBot(name, group.name()); + // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. + const std::size_t num_ancestors = chain.size() - 1; + + // check whether guide rate is violated + if (check_guide_rate) { + for (std::size_t ii = 1; ii < num_ancestors; ++ii) { + if (this->guide_rate_.has(chain[ii])) { + const auto& guided_group = chain[ii]; + const Scalar grefficiency = + this->schedule_.getGroup(guided_group, this->report_step_).getGroupEfficiencyFactor(); + const Scalar current_rate_fraction = + grefficiency * local_current_rate_lambda(guided_group) / + (local_current_rate_lambda(chain[ii-1])); + const Scalar guiderate_fraction = local_fraction_lambda(guided_group); + // we add a factor here to avoid switching due to numerical instability + const Scalar factor = 1.01; + if (current_rate_fraction > (guiderate_fraction * factor)) { + return std::make_pair(true, guiderate_fraction / current_rate_fraction); + } + } + } + } + + if (this->schedule_.hasWell(name) && this->wellState().well(name).group_target) { + // for wells we already have computed the target + + // Switch sign since 'rates' are negative for producers. + const Scalar current_well_rate_available = -tcalc.calcModeRateFromRates(rates); + const Scalar group_target_rate_available = *this->wellState().well(name).group_target; + Scalar scale = 1.0; + if (current_well_rate_available > 1e-12) { + scale = group_target_rate_available / current_well_rate_available; + } + + return std::make_pair(current_well_rate_available > group_target_rate_available, scale); + } + + // we need to find out the level where the current well is applied to the local reduction + std::size_t local_reduction_level = 0; + for (std::size_t ii = 1; ii < num_ancestors; ++ii) { + const int num_gr_ctrl = this->groupControlledWells( + chain[ii], + /*always_included_child=*/"", + /*is_producer=*/true, + /*injection_phase_not_used=*/Phase::OIL + ); + if (this->guide_rate_.has(chain[ii]) && num_gr_ctrl > 0) { + local_reduction_level = ii; + } + } + + // Compute portion of target corresponding to current_rate_available + Scalar target = orig_target; + for (std::size_t ii = 0; ii < num_ancestors; ++ii) { + if ((ii == 0) || this->guide_rate_.has(chain[ii])) { + // Apply local reductions only at the control level + // (top) and for levels where we have a specified + // group guide rate. + if (local_reduction_level >= ii ) { + target -= local_reduction_lambda(chain[ii]); + } + // Add my reduction back at the level where it is included in the local reduction + if (local_reduction_level == ii ) { + target += current_rate_available * efficiency_factor; + } + } + target *= local_fraction_lambda(chain[ii + 1]); + } + // Avoid negative target rates comming from too large local reductions. + const Scalar target_rate_available = std::max(Scalar(1e-12), target / efficiency_factor); + + Scalar scale = 1.0; + if (current_rate_available > 1e-12) + scale = target_rate_available / current_rate_available; + + return std::make_pair(current_rate_available > target_rate_available, scale); +} + +template +std::map +WellGroupHelper:: +computeNetworkPressures(const Network::ExtNetwork& network, + const VFPProdProperties& vfp_prod_props, + const Parallel::Communication& comm) const +{ + // TODO: Only dealing with production networks for now. + OPM_TIMEFUNCTION(); + if (!network.active()) { + return {}; + } + + std::map node_pressures; + auto roots = network.roots(); + for (const auto& root : roots) { + // Fixed pressure nodes of the network are the roots of trees. + // Leaf nodes must correspond to groups in the group structure. + // Let us first find all leaf nodes of the network. We also + // create a vector of all nodes, ordered so that a child is + // always after its parent. + std::stack children; + std::set leaf_nodes; + std::vector root_to_child_nodes; + //children.push(network.root().name()); + children.push(root.get().name()); + while (!children.empty()) { + const auto node = children.top(); + children.pop(); + root_to_child_nodes.push_back(node); + auto branches = network.downtree_branches(node); + if (branches.empty()) { + leaf_nodes.insert(node); + } + for (const auto& branch : branches) { + children.push(branch.downtree_node()); + } + } + assert(children.empty()); + + // Starting with the leaf nodes of the network, get the flow rates + // from the corresponding groups. + std::map> node_inflows; + const std::vector zero_rates(3, 0.0); + for (const auto& node : leaf_nodes) { + // Guard against empty leaf nodes (may not be present in GRUPTREE) + if (!this->groupState().has_production_rates(node)) { + node_inflows[node] = zero_rates; + continue; + } + + node_inflows[node] = this->groupState().network_leaf_node_production_rates(node); + // Add the ALQ amounts to the gas rates if requested. + if (network.node(node).add_gas_lift_gas()) { + const auto& group = this->schedule_.getGroup(node, this->report_step_); + Scalar alq = 0.0; + for (const std::string& wellname : group.wells()) { + const Well& well = this->schedule_.getWell(wellname, this->report_step_); + if (well.isInjector() || !this->wellState().isOpen(wellname)) continue; + const Scalar efficiency = well.getEfficiencyFactor(/*network*/ true) * + this->wellState().getGlobalEfficiencyScalingFactor(wellname); + const auto& well_index = this->wellState().index(wellname); + if (well_index.has_value() && this->wellState().wellIsOwned(well_index.value(), wellname)) { + alq += this->wellState().well(wellname).alq_state.get() * efficiency; + } + } + alq = comm.sum(alq); + node_inflows[node][IndexTraits::gasPhaseIdx] += alq; + } + } + + // Accumulate in the network, towards the roots. Note that a + // root (i.e. fixed pressure node) can still be contributing + // flow towards other nodes in the network, i.e. a node is + // the root of a subtree. + auto child_to_root_nodes = root_to_child_nodes; + std::reverse(child_to_root_nodes.begin(), child_to_root_nodes.end()); + for (const auto& node : child_to_root_nodes) { + const auto upbranch = network.uptree_branch(node); + if (upbranch) { + // Add downbranch rates to upbranch. + std::vector& up = node_inflows[(*upbranch).uptree_node()]; + const std::vector& down = node_inflows[node]; + // We now also support NEFAC + const Scalar efficiency = network.node(node).efficiency(); + if (up.empty()) { + up = std::vector(down.size(), 0.0); + } + assert (up.size() == down.size()); + for (std::size_t ii = 0; ii < up.size(); ++ii) { + up[ii] += efficiency*down[ii]; + } + } + } + + // Going the other way (from roots to leafs), calculate the pressure + // at each node using VFP tables and rates. + //std::map node_pressures; + for (const auto& node : root_to_child_nodes) { + auto press = network.node(node).terminal_pressure(); + if (press) { + node_pressures[node] = *press; + } else { + const auto upbranch = network.uptree_branch(node); + assert(upbranch); + const Scalar up_press = node_pressures[(*upbranch).uptree_node()]; + const auto vfp_table = (*upbranch).vfp_table(); + if (vfp_table) { + OPM_TIMEBLOCK(NetworkVfpCalculations); + // The rates are here positive, but the VFP code expects the + // convention that production rates are negative, so we must + // take a copy and flip signs. + auto rates = node_inflows[node]; + std::transform(rates.begin(), rates.end(), rates.begin(), + [](const auto r) { return -r; }); + assert(rates.size() == 3); + // NB! ALQ in extended network is never implicitly the gas lift rate (GRAT), i.e., the + // gas lift rates only enters the network pressure calculations through the rates + // (e.g., in GOR calculations) unless a branch ALQ is set in BRANPROP. + // + // @TODO: Standard network + Scalar alq = (*upbranch).alq_value().value_or(0.0); + node_pressures[node] = vfp_prod_props.bhp(*vfp_table, + rates[IndexTraits::waterPhaseIdx], + rates[IndexTraits::oilPhaseIdx], + rates[IndexTraits::gasPhaseIdx], + up_press, + alq, + 0.0, //explicit_wfr + 0.0, //explicit_gfr + false); //use_expvfp we dont support explicit lookup +#define EXTRA_DEBUG_NETWORK 0 +#if EXTRA_DEBUG_NETWORK + std::ostringstream oss; + oss << "parent: " << (*upbranch).uptree_node() << " child: " << node + << " rates = [ " << rates[0]*86400 << ", " << rates[1]*86400 << ", " << rates[2]*86400 << " ]" + << " p(parent) = " << up_press/1e5 << " p(child) = " << node_pressures[node]/1e5 << std::endl; + OpmLog::debug(oss.str()); +#endif + } else { + // Table number specified as 9999 in the deck, no pressure loss. + if (network.node(node).as_choke()){ + // Node pressure is set to the group THP. + node_pressures[node] = this->groupState().well_group_thp(node); + } else { + node_pressures[node] = up_press; + } + } + } + } + } + return node_pressures; +} + +template +Scalar +WellGroupHelper:: +getGuideRate(const std::string& name, const GuideRateModel::Target target) const +{ + if (this->schedule_.hasWell(name, this->report_step_)) { + if (this->guide_rate_.has(name) || this->guide_rate_.hasPotentials(name)) { + return this->guide_rate_.get(name, target, this->getWellRateVector(name)); + } else { + return 0.0; + } + } + + if (this->guide_rate_.has(name)) { + return this->guide_rate_.get(name, target, this->getProductionGroupRateVector(name)); + } + + Scalar total_guide_rate = 0.0; + const Group& group = this->schedule_.getGroup(name, this->report_step_); + + for (const std::string& group_name : group.groups()) { + const Group::ProductionCMode& current_group_control + = this->groupState().production_control(group_name); + if (current_group_control == Group::ProductionCMode::FLD + || current_group_control == Group::ProductionCMode::NONE) { + // accumulate from sub wells/groups + total_guide_rate += this->getGuideRate(group_name, target); + } + } + + for (const std::string& well_name : group.wells()) { + const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_); + + if (well_tmp.isInjector()) + continue; + + const auto well_index = this->wellState().index(well_name); + if (!well_index.has_value()) + continue; + + if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + { + continue; + } + + const auto& ws = this->wellState().well(well_index.value()); + if (ws.status == Well::Status::SHUT) + continue; + + if (! this->wellState().isProductionGrup(well_name)) + continue; + + // Only count wells under group control or the ru + if (!this->wellState().isProductionGrup(well_name)) + continue; + + total_guide_rate += this->getGuideRate(well_name, target); + + } + return total_guide_rate; +} + +template +GuideRate::RateVector +WellGroupHelper:: +getProductionGroupRateVector(const std::string& group_name) const +{ + return this->getGuideRateVector_(this->groupState().production_rates(group_name)); +} + +template +Scalar +WellGroupHelper:: +getWellGroupTargetInjector(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff) +{ + // This function computes a wells group target. + // 'parent' will be the name of 'group'. But if we recurse, 'name' and + // 'parent' will stay fixed while 'group' will be higher up + // in the group tree. + // Eficiencyfactor is the well efficiency factor for the first group the well is + // part of. Later it is the accumulated factor including the group efficiency factor + // of the child of group. + auto current_group_control = this->groupState().injection_control(group.name(), injection_phase); + if (current_group_control == Group::InjectionCMode::FLD || current_group_control == Group::InjectionCMode::NONE) { + // Return if we are not available for parent group. + if (!group.injectionGroupControlAvailable(injection_phase)) { + return std::numeric_limits::max(); + } + // Otherwise: check production share of parent's control. + const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); + return this->getWellGroupTargetInjector(name, + parent, + parent_group, + rates, + injection_phase, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff); + } + + // This can be false for FLD-controlled groups, we must therefore + // check for FLD first (done above). + if (!group.isInjectionGroup()) { + return std::numeric_limits::max(); + } + + // If we are here, we are at the topmost group to be visited in the recursion. + // This is the group containing the control we will check against. + Scalar sales_target = 0; + if (this->schedule_[this->report_step_].gconsale().has(group.name())) { + const auto& gconsale = + this->schedule_[this->report_step_].gconsale().get(group.name(), this->summary_state_); + sales_target = gconsale.sales_target; + } + WGHelpers::InjectionTargetCalculator tcalc{ + current_group_control, + this->phase_usage_info_, + resv_coeff, + group.name(), + sales_target, + this->groupState(), + injection_phase, + group.has_gpmaint_control(injection_phase, current_group_control), + this->deferredLogger() + }; + + WGHelpers::FractionCalculator fcalc{ + this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + /*is_production_group=*/false, + injection_phase + }; + + auto local_fraction_lambda = [&](const std::string& child, const std::string& always_incluced_name) { + return fcalc.localFraction(child, always_incluced_name); + }; + + auto local_reduction_lambda = [&](const std::string& group_name) + { + const std::vector& group_target_reductions = + this->groupState().injection_reduction_rates(group_name); + return tcalc.calcModeRateFromRates(group_target_reductions); + }; + + std::optional ctrl; + if (!group.has_gpmaint_control(injection_phase, current_group_control)) + ctrl = group.injectionControls(injection_phase, this->summary_state_); + + + const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. + // Then ... + // TODO finish explanation. + const Scalar current_rate_available + = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. + const auto chain = this->groupChainTopBot(name, group.name()); + // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. + const std::size_t num_ancestors = chain.size() - 1; + // we need to find out the level where the local reduction is applied + std::size_t local_reduction_level = 0; + for (std::size_t ii = 1; ii < num_ancestors; ++ii) { + const int num_gr_ctrl = this->groupControlledWells( + chain[ii], + /*always_included_child=*/"", + /*is_production_group=*/false, + injection_phase + ); + if (this->guide_rate_.has(chain[ii], injection_phase) && num_gr_ctrl > 0) { + local_reduction_level = ii; + } + } + + // Compute portion of target corresponding to current_rate_available + Scalar target = orig_target; + for (std::size_t ii = 0; ii < num_ancestors; ++ii) { + if ((ii == 0) || this->guide_rate_.has(chain[ii], injection_phase)) { + // Apply local reductions only at the control level + // (top) and for levels where we have a specified + // group guide rate. + if (local_reduction_level >= ii ) { + target -= local_reduction_lambda(chain[ii]); + } + + // If we are under individual control we need to add the wells rate back at the level where it is included in the local reduction + if (local_reduction_level == ii && !this->wellState().isInjectionGrup(name)) { + target += current_rate_available * efficiency_factor; + } + } + if (!this->wellState().isInjectionGrup(name)) { + target *= local_fraction_lambda(chain[ii + 1], name); + } else { + target *= local_fraction_lambda(chain[ii + 1], chain[ii + 1]); + } + } + // Avoid negative target rates comming from too large local reductions. + return std::max(Scalar(0.0), target / efficiency_factor); +} + +template +Scalar +WellGroupHelper:: +getWellGroupTargetProducer(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff) +{ + // This function computes a wells group target. + // 'parent' will be the name of 'group'. But if we recurse, 'name' and + // 'parent' will stay fixed while 'group' will be higher up + // in the group tree. + // Eficiencyfactor is the well efficiency factor for the first group the well is + // part of. Later it is the accumulated factor including the group efficiency factor + // of the child of group. + OPM_TIMEFUNCTION(); + const Group::ProductionCMode& current_group_control = + this->groupState().production_control(group.name()); + + if (current_group_control == Group::ProductionCMode::FLD || current_group_control == Group::ProductionCMode::NONE) { + // Return if we are not available for parent group. + if (!group.productionGroupControlAvailable()) { + return std::numeric_limits::max(); + } + // Otherwise: check production share of parent's control. + const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); + return this->getWellGroupTargetProducer(name, + parent, + parent_group, + rates, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff); + } + + // This can be false for FLD-controlled groups, we must therefore + // check for FLD first (done above). + if (!group.isProductionGroup()) { + return std::numeric_limits::max(); + } + + // If we are here, we are at the topmost group to be visited in the recursion. + // This is the group containing the control we will check against. + + // gconsale may adjust the grat target. + // the adjusted rates is send to the targetCalculator + Scalar grat_target_from_sales = 0.0; + if (this->groupState().has_grat_sales_target(group.name())) + grat_target_from_sales = this->groupState().grat_sales_target(group.name()); + + WGHelpers::TargetCalculator tcalc{ + current_group_control, + this->phase_usage_info_, + resv_coeff, + grat_target_from_sales, + group.name(), + this->groupState(), + group.has_gpmaint_control(current_group_control) + }; + + WGHelpers::FractionCalculator fcalc{ + this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + true, + Phase::OIL + }; + auto local_fraction_lambda = [&](const std::string& child, const std::string& always_incluced_name) { + return fcalc.localFraction(child, always_incluced_name); + }; + + auto local_reduction_lambda = [&](const std::string& group_name) + { + const std::vector& group_target_reductions = + this->groupState().production_reduction_rates(group_name); + return tcalc.calcModeRateFromRates(group_target_reductions); + }; + + std::optional ctrl; + if (!group.has_gpmaint_control(current_group_control)) + ctrl = group.productionControls(this->summary_state_); + + const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. + // Then ... + // TODO finish explanation. + const Scalar current_rate_available + = -tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. + const auto chain = this->groupChainTopBot(name, group.name()); + // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. + const std::size_t num_ancestors = chain.size() - 1; + // we need to find out the level where the local reduction is applied + std::size_t local_reduction_level = 0; + for (std::size_t ii = 1; ii < num_ancestors; ++ii) { + const int num_gr_ctrl = this->groupControlledWells( + chain[ii], + /*always_included_child=*/"", + /*is_production_group=*/true, + /*injection_phase=*/Phase::OIL + ); + if (this->guide_rate_.has(chain[ii]) && num_gr_ctrl > 0) { + local_reduction_level = ii; + } + } + // Compute portion of target corresponding to current_rate_available + Scalar target = orig_target; + for (std::size_t ii = 0; ii < num_ancestors; ++ii) { + if ((ii == 0) || this->guide_rate_.has(chain[ii])) { + // Apply local reductions only at the control level + // (top) and for levels where we have a specified + // group guide rate. + if (local_reduction_level >= ii) { + target -= local_reduction_lambda(chain[ii]); + } + // If we are under individual control we need to add the wells rate back at the level where it is included in the local reduction + if (local_reduction_level == ii && !this->wellState().isProductionGrup(name)) { + target += current_rate_available * efficiency_factor; + } + } + if (this->wellState().isProductionGrup(name)) { + target *= local_fraction_lambda(chain[ii + 1], chain[ii + 1]); + } else { + target *= local_fraction_lambda(chain[ii + 1], name); + } + } + // Avoid negative target rates comming from too large local reductions. + return std::max(Scalar(0.0), target / efficiency_factor);; +} + +template +GuideRate::RateVector +WellGroupHelper:: +getWellRateVector(const std::string& name) const +{ + return this->getGuideRateVector_(this->wellState().currentWellRates(name)); +} + +template +std::vector +WellGroupHelper:: +groupChainTopBot(const std::string& bottom, const std::string& top) const +{ + // Get initial parent, 'bottom' can be a well or a group. + std::string parent; + if (this->schedule_.hasWell(bottom, this->report_step_)) { + parent = this->schedule_.getWell(bottom, this->report_step_).groupName(); + } else { + parent = this->schedule_.getGroup(bottom, this->report_step_).parent(); + } + + // Build the chain from bottom to top. + std::vector chain; + chain.push_back(bottom); + chain.push_back(parent); + while (parent != top) { + parent = this->schedule_.getGroup(parent, this->report_step_).parent(); + chain.push_back(parent); + } + assert(chain.back() == top); + + // Reverse order and return. + std::reverse(chain.begin(), chain.end()); + return chain; +} + +template +int WellGroupHelper:: +groupControlledWells(const std::string& group_name, + const std::string& always_included_child, + const bool is_production_group, + const Phase injection_phase) const +{ + auto num_wells = is_production_group ? this->groupState().number_of_wells_under_group_control(group_name) + : this->groupState().number_of_wells_under_inj_group_control(group_name, injection_phase); + if (this->schedule_.hasWell(always_included_child, this->report_step_)) { + const bool isInGroup = this->isInGroupChainTopBot_(always_included_child, group_name); + const bool already_included = is_production_group + ? this->wellState().isProductionGrup(always_included_child) + : this->wellState().isInjectionGrup(always_included_child); + if (!already_included && isInGroup) { + num_wells++; + } + } + return num_wells; +} + +template +void WellGroupHelper:: +setCmodeGroup(const Group& group) +{ + + for (const std::string& group_name : group.groups()) { + this->setCmodeGroup(this->schedule_.getGroup(group_name, this->report_step_)); + } + + // use NONE as default control + const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; + for (Phase phase : all) { + if (!this->groupState().has_injection_control(group.name(), phase)) { + this->groupState().injection_control(group.name(), phase, Group::InjectionCMode::NONE); + } + } + if (!this->groupState().has_production_control(group.name())) { + this->groupState().production_control(group.name(), Group::ProductionCMode::NONE); + } + + const auto& events = this->schedule_[this->report_step_].wellgroup_events(); + if (group.isInjectionGroup() + && events.hasEvent(group.name(), ScheduleEvents::GROUP_INJECTION_UPDATE)) { + + for (Phase phase : all) { + if (!group.hasInjectionControl(phase)) + continue; + + const auto& controls = group.injectionControls(phase, this->summary_state_); + this->groupState().injection_control(group.name(), phase, controls.cmode); + } + } + + if (group.isProductionGroup() + && events.hasEvent(group.name(), ScheduleEvents::GROUP_PRODUCTION_UPDATE)) { + const auto controls = group.productionControls(this->summary_state_); + this->groupState().production_control(group.name(), controls.cmode); + } + + if (group.has_gpmaint_control(Group::ProductionCMode::RESV)) { + this->groupState().production_control(group.name(), Group::ProductionCMode::RESV); + } + for (Phase phase : all) { + if (group.has_gpmaint_control(phase, Group::InjectionCMode::RATE)) { + this->groupState().injection_control(group.name(), phase, Group::InjectionCMode::RATE); + } else if (group.has_gpmaint_control(phase, Group::InjectionCMode::RESV)) { + this->groupState().injection_control(group.name(), phase, Group::InjectionCMode::RESV); + } + } + + if (this->schedule_[this->report_step_].gconsale().has(group.name())) { + this->groupState().injection_control(group.name(), Phase::GAS, Group::InjectionCMode::SALE); + } +} + +// NOTE: setRegionAveragePressureCalculator() is a template member function and must be +// defined in the header file (see WellGroupHelper.hpp). +// +// See: updateGpMaintTargetForGroups() for detailed rationale. + +template +Scalar +WellGroupHelper:: +sumSolventRates(const Group& group, const bool is_injector) +{ + Scalar rate = 0.0; + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + const auto& gefac = group_tmp.getGroupEfficiencyFactor(); + rate += gefac * this->sumSolventRates(group_tmp, is_injector); + } + + for (const std::string& well_name : group.wells()) { + const auto well_index = this->wellState().index(well_name); + if (!well_index.has_value()) + continue; + + if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + { + continue; + } + + const auto& well_ecl = this->schedule_.getWell(well_name, this->report_step_); + // only count producers or injectors + if ((well_ecl.isProducer() && is_injector) || (well_ecl.isInjector() && !is_injector)) + continue; + + const auto& ws = this->wellState().well(well_index.value()); + + if (ws.status == Well::Status::SHUT) + continue; + + const Scalar factor = well_ecl.getEfficiencyFactor() * + this->wellState()[well_ecl.name()].efficiency_scaling_factor; + if (is_injector) + rate += factor * ws.sum_solvent_rates(); + else + rate -= factor * ws.sum_solvent_rates(); + } + return rate; +} + +template +Scalar WellGroupHelper:: +sumWellPhaseRates(bool res_rates, + const Opm::Group& group, + const int phase_pos, + const bool injector, + const bool network) const +{ + // Only obtain satellite rates once (on rank 0) + Scalar rate = 0.0; + if (this->wellState().isRank0() && (group.hasSatelliteProduction() || group.hasSatelliteInjection())) { + if (injector) { + rate = this->satelliteInjectionRate_( + this->schedule_[this->report_step_], + group, + phase_pos, + res_rates + ); + } else { + const auto rate_comp = this->selectRateComponent_(phase_pos); + if (rate_comp.has_value()) { + rate = this->satelliteProductionRate_( + this->schedule_[this->report_step_], group, *rate_comp, res_rates + ); + } + } + // Satellite groups have no sub groups/wells so we're done + return rate; + } + + for (const std::string& group_name : group.groups()) { + const auto& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + const auto& gefac = group_tmp.getGroupEfficiencyFactor(network); + rate += gefac * this->sumWellPhaseRates(res_rates, group_tmp, phase_pos, injector, network); + } + + for (const std::string& well_name : group.wells()) { + const auto well_index = this->wellState().index(well_name); + if (!well_index.has_value()) + continue; + + if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + { + continue; + } + + const auto& well_ecl = this->schedule_.getWell(well_name, this->report_step_); + // only count producers or injectors + if ((well_ecl.isProducer() && injector) || (well_ecl.isInjector() && !injector)) + continue; + + const auto& ws = this->wellState().well(well_index.value()); + if (ws.status == Opm::Well::Status::SHUT) + continue; + + const Scalar factor = well_ecl.getEfficiencyFactor(network) + * this->wellState().well(well_index.value()).efficiency_scaling_factor; + if (res_rates) { + const auto& well_rates = ws.reservoir_rates; + if (injector) + rate += factor * well_rates[phase_pos]; + else + rate -= factor * well_rates[phase_pos]; + } else { + const auto& well_rates = ws.surface_rates; + if (injector) + rate += factor * well_rates[phase_pos]; + else + rate -= factor * well_rates[phase_pos]; + } + } + return rate; +} + +template +Scalar +WellGroupHelper:: +sumWellResRates(const Group& group, const int phasePos, const bool injector) const +{ + return this->sumWellPhaseRates(/*res_rates=*/true, group, phasePos, injector); +} + +template +Scalar +WellGroupHelper:: +sumWellSurfaceRates(const Group& group, + const int phase_pos, + const bool injector) const +{ + return this->sumWellPhaseRates(/*res_rates=*/false, group, phase_pos, injector); +} + +// NOTE: updateGpMaintTargetForGroups() is a template member function and must be +// defined in the header file (see WellGroupHelper.hpp). +// +// Rationale: +// - The RegionalValues template parameter depends on BlackoilWellModel's FluidSystem, +// which varies by derived class and is not available in this compilation unit. +// - Template functions must be visible at their point of instantiation, so the +// implementation cannot be in this .cpp file. +// - We cannot use explicit template instantiation because the specific RegionalValues +// type depends on types from derived classes (e.g., AverageRegionalPressure). +// +// Historical Note: +// - The original implementation in WellGroupHelpers.cpp (static utility class) could keep +// the implementation in the .cpp file because it hard-coded BlackOilFluidSystem +// and used explicit template instantiation for that specific type. +// - As a member-based class, WellGroupHelper must support whatever FluidSystem the TypeTag +// provides, so we cannot hard-code the type and must use header-based implementation. +// +// If moved here, the linker will report: +// undefined reference to +// `WellGroupHelper<...>::updateGpMaintTargetForGroups>(const Group&, ...)` + +template +int WellGroupHelper:: +updateGroupControlledWells(const bool is_production_group, const Phase injection_phase) +{ + OPM_TIMEFUNCTION(); + const auto& group_name = "FIELD"; + return this->updateGroupControlledWellsRecursive_(group_name, is_production_group, injection_phase); +} + +template +void WellGroupHelper:: +updateGroupProductionRates(const Group& group) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateGroupProductionRates(group_tmp); + } + const int np = this->wellState().numPhases(); + std::vector rates(np, 0.0); + for (int phase = 0; phase < np; ++phase) { + rates[phase] = this->sumWellPhaseRates( + /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false + ); + } + this->groupState().update_production_rates(group.name(), rates); +} + +template +void WellGroupHelper:: +updateGroupTargetReduction(const Group& group, const bool is_injector) +{ + OPM_TIMEFUNCTION(); + std::vector group_target_reduction(this->wellState().numPhases(), 0.0); + this->updateGroupTargetReductionRecursive_(group, is_injector, group_target_reduction); +} + +template +void WellGroupHelper:: +updateNetworkLeafNodeProductionRates() +{ + const auto& network = this->schedule_[this->report_step_].network(); + if (network.active()) { + const int np = this->wellState().numPhases(); + for (const auto& group_name : network.leaf_nodes()) { + std::vector network_rates(np, 0.0); + if (this->schedule_[this->report_step_].groups.has(group_name)) { // Allow empty leaf nodes that are not groups + const auto& group = this->schedule_[this->report_step_].groups.get(group_name); + if (group.numWells() > 0) { + for (int phase = 0; phase < np; ++phase) { + network_rates[phase] = this->sumWellPhaseRates( + /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/true + ); + } + } + } + this->groupState().update_network_leaf_node_production_rates(group_name, network_rates); + } + } +} + +template +void WellGroupHelper:: +updateREINForGroups(const Group& group, bool sum_rank) +{ + OPM_TIMEFUNCTION(); + const int np = this->wellState().numPhases(); + for (const std::string& groupName : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(groupName, this->report_step_); + this->updateREINForGroups(group_tmp, sum_rank); + } + + std::vector rein(np, 0.0); + for (int phase = 0; phase < np; ++phase) { + rein[phase] = this->sumWellPhaseRates( + /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false + ); + } + + // add import rate and subtract consumption rate for group for gas + if (sum_rank) { + const auto& pu = this->wellState().phaseUsageInfo(); + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + const auto& [consumption_rate, import_rate] = this->groupState().gconsump_rates(group.name()); + const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + rein[ig] += import_rate; + rein[ig] -= consumption_rate; + } + } + + this->groupState().update_injection_rein_rates(group.name(), rein); +} + +template +void WellGroupHelper:: +updateReservoirRatesInjectionGroups(const Group& group) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateReservoirRatesInjectionGroups(group_tmp); + } + const int np = this->wellState().numPhases(); + std::vector resv(np, 0.0); + for (int phase = 0; phase < np; ++phase) { + resv[phase] = this->sumWellPhaseRates( + /*res_rates=*/true, group, phase, /*injector=*/true, /*network=*/false + ); + } + this->groupState().update_injection_reservoir_rates(group.name(), resv); +} + +template +void WellGroupHelper:: +updateState(WellState& well_state, GroupState& group_state, int report_step) +{ + this->well_state_ = &well_state; + this->group_state_ = &group_state; + this->report_step_ = report_step; +} + +template +void WellGroupHelper:: +updateSurfaceRatesInjectionGroups(const Group& group) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateSurfaceRatesInjectionGroups(group_tmp); + } + const int np = this->wellState().numPhases(); + std::vector rates(np, 0.0); + for (int phase = 0; phase < np; ++phase) { + rates[phase] = this->sumWellPhaseRates( + /*res_rates=*/false, + group, + phase, + /*injector=*/true, + /*network=*/false + ); + } + this->groupState().update_injection_surface_rates(group.name(), rates); +} + + +template +void WellGroupHelper:: +updateVREPForGroups(const Group& group) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateVREPForGroups(group_tmp); + } + const int np = this->wellState().numPhases(); + Scalar resv = 0.0; + for (int phase = 0; phase < np; ++phase) { + resv += this->sumWellPhaseRates( + /*res_rates=*/true, group, phase, /*injector=*/false, /*network=*/false + ); + } + this->groupState().update_injection_vrep_rate(group.name(), resv); +} + +template +void WellGroupHelper:: +updateWellRates(const Group& group, const WellState& well_state_nupcol) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateWellRates(group_tmp, well_state_nupcol); + } + const int np = this->wellState().numPhases(); + for (const std::string& well_name : group.wells()) { + std::vector rates(np, 0.0); + const auto well_index = this->wellState().index(well_name); + if (well_index.has_value()) { // the well is found on this node + const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_); + int sign = 1; + // production wellRates are negative. The users of currentWellRates uses the convention in + // opm-common that production and injection rates are positive. + if (!well_tmp.isInjector()) + sign = -1; + const auto& ws = well_state_nupcol.well(well_index.value()); + for (int phase = 0; phase < np; ++phase) { + rates[phase] = sign * ws.surface_rates[phase]; + } + } + this->wellState().setCurrentWellRates(well_name, rates); + } +} + +template +void WellGroupHelper:: +updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool is_injector) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + bool individual_control = false; + if (is_injector) { + const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; + for (Phase phase : all) { + const Group::InjectionCMode& current_group_control = + this->groupState().injection_control(group_name, phase); + individual_control = individual_control || + (current_group_control != Group::InjectionCMode::FLD && + current_group_control != Group::InjectionCMode::NONE); + } + } else { + const Group::ProductionCMode& current_group_control = + this->groupState().production_control(group_name); + individual_control = (current_group_control != Group::ProductionCMode::FLD + && current_group_control != Group::ProductionCMode::NONE); + } + if (!individual_control) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateWellRatesFromGroupTargetScale(scale, group_tmp, is_injector); + } + } + + const int np = this->wellState().numPhases(); + for (const std::string& well_name : group.wells()) { + const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_); + + if (well_tmp.isProducer() && is_injector) + continue; + + if (well_tmp.isInjector() && !is_injector) + continue; + + const auto well_index = this->wellState().index(well_name); + if (!well_index.has_value()) + continue; + + if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + { + continue; + } + + auto& ws = this->wellState().well(well_index.value()); + if (ws.status == Well::Status::SHUT) + continue; + + // scale rates + if (is_injector) { + if (ws.injection_cmode == Well::InjectorCMode::GRUP) + for (int phase = 0; phase < np; phase++) { + ws.surface_rates[phase] *= scale; + } + } else { + if (ws.production_cmode == Well::ProducerCMode::GRUP) { + for (int phase = 0; phase < np; phase++) { + ws.surface_rates[phase] *= scale; + } + } + } + } +} + +template +std::pair, Scalar> +WellGroupHelper:: +worstOffendingWell(const Group& group, + const Group::ProductionCMode& offended_control, + const Parallel::Communication& comm) const +{ + std::pair, Scalar> offending_well {std::nullopt, 0.0}; + for (const std::string& child_group : group.groups()) { + const auto& this_group = this->schedule_.getGroup(child_group, this->report_step_); + const auto & offending_well_this = this->worstOffendingWell( + this_group, offended_control, comm + ); + if (offending_well_this.second > offending_well.second) { + offending_well = offending_well_this; + } + } + + for (const std::string& child_well : group.wells()) { + + const auto& well_index = this->wellState().index(child_well); + Scalar violating_rate = 0.0; + Scalar prefered_rate = 0.0; + const auto& pu = this->phase_usage_info_; + if (well_index.has_value() && this->wellState().wellIsOwned(well_index.value(), child_well)) + { + const auto& ws = this->wellState().well(child_well); + // Cannot use deferredLogger() accessor here since it's non-const and this method is const. + // Direct pointer dereference is safe as the logger object itself is mutable. + auto& deferred_logger = *this->deferred_logger_; + switch (offended_control){ + case Group::ProductionCMode::ORAT: { + const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + violating_rate = ws.surface_rates[oil_pos]; + break; + } + case Group::ProductionCMode::GRAT: { + const int gas_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + violating_rate = ws.surface_rates[gas_pos]; + break; + } + case Group::ProductionCMode::WRAT: { + const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + violating_rate = ws.surface_rates[water_pos]; + break; + } + case Group::ProductionCMode::LRAT: { + assert(pu.phaseIsActive(IndexTraits::oilPhaseIdx)); + assert(pu.phaseIsActive(IndexTraits::waterPhaseIdx)); + const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + violating_rate = ws.surface_rates[oil_pos] + + ws.surface_rates[water_pos]; + break; + } + case Group::ProductionCMode::RESV: + for (int p = 0; p < this->wellState().numPhases(); ++p) { + violating_rate += ws.reservoir_rates[p]; + } + break; + case Group::ProductionCMode::NONE: + break; + case Group::ProductionCMode::FLD: + break; + case Group::ProductionCMode::PRBL: + OPM_DEFLOG_THROW(std::runtime_error, + "Group " + group.name() + + " GroupProductionCMode PRBL not implemented", + deferred_logger); + break; + case Group::ProductionCMode::CRAT: + OPM_DEFLOG_THROW(std::runtime_error, + "Group " + group.name() + + " GroupProductionCMode CRAT not implemented", + deferred_logger); + break; + } + const auto preferred_phase = + this->schedule_.getWell(child_well, this->report_step_).getPreferredPhase(); + switch (preferred_phase) { + case Phase::OIL: { + const int oil_pos = + this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + prefered_rate = ws.surface_rates[oil_pos]; + break; + } + case Phase::GAS: { + const int gas_pos = + this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + prefered_rate = ws.surface_rates[gas_pos]; + break; + } + case Phase::WATER: { + const int water_pos = + this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + prefered_rate = ws.surface_rates[water_pos]; + break; + } + default: + // No others supported. + break; + } + } + violating_rate = comm.sum(violating_rate); + if (violating_rate < 0 ) { // only check producing wells + prefered_rate = comm.sum(prefered_rate); + Scalar fraction = prefered_rate < -1e-16 ? violating_rate / prefered_rate : 1.0; + if ( fraction > offending_well.second) { + offending_well = {child_well, fraction}; + } + } + } + return offending_well; +} + +// --------------------------------------------------------------------- +// Private methods +// --------------------------------------------------------------------- + +template +std::string +WellGroupHelper:: +controlGroup_(const Group& group) +{ + const Group::ProductionCMode& currentGroupControl = this->groupState().production_control(group.name()); + + if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) { + const auto& parent_name = group.control_group(); + if (parent_name) { + const auto& parent = this->schedule_.getGroup(parent_name.value(), this->report_step_); + return this->controlGroup_(parent); + } + } + + return group.name(); +} + +template +Opm::GuideRate::RateVector +WellGroupHelper:: +getGuideRateVector_(const std::vector& rates) const +{ + const auto& pu = this->phase_usage_info_; + Scalar oilRate = 0.0; + if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { + oilRate = rates[pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx)]; + } + + Scalar gasRate = 0.0; + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + gasRate = rates[pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx)]; + } + + Scalar waterRate = 0.0; + if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { + waterRate = rates[pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx)]; + } + + return {oilRate, gasRate, waterRate}; +} + +template +bool +WellGroupHelper:: +isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const +{ + // Get initial parent, 'bottom' can be a well or a group. + std::string parent; + if (this->schedule_.hasWell(bottom, this->report_step_)) { + parent = this->schedule_.getWell(bottom, this->report_step_).groupName(); + } else { + parent = this->schedule_.getGroup(bottom, this->report_step_).parent(); + } + + while (parent != top) { + parent = this->schedule_.getGroup(parent, this->report_step_).parent(); + if (parent == top) { + return true; + } else if (parent == "FIELD") { + return false; + } + } + return true; +} + +template +int WellGroupHelper:: +phaseToActivePhaseIdx_(const Phase phase) +{ + const auto& pu = this->phase_usage_info_; + int phase_pos = -1; + switch (phase) { + case Phase::GAS: + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + phase_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + } + break; + case Phase::OIL: + if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { + phase_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + } + break; + case Phase::WATER: + if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { + phase_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + } + break; + default: + // just to avoid warning + throw std::invalid_argument("unhandled phase enum"); + } + return phase_pos; +} + +template +Scalar WellGroupHelper:: +satelliteInjectionRate_(const ScheduleState& sched, + const Group& group, + const int phase_pos, + bool res_rates) const +{ + Scalar rate = 0.0; + const auto& pu = this->phase_usage_info_; + if (group.hasSatelliteInjection()) { + std::optional ph; + for (const auto& [bo_phase, phase] : std::array { + std::pair( IndexTraits::waterPhaseIdx, Phase::WATER), + std::pair( IndexTraits::oilPhaseIdx, Phase::OIL), + std::pair( IndexTraits::gasPhaseIdx, Phase::GAS) }) + { + if (pu.phaseIsActive(bo_phase) && (pu.canonicalToActivePhaseIdx(bo_phase) == phase_pos)) { + ph = phase; + } + } + if (ph.has_value()) { + const auto& satellite_inj = sched.satelliteInjection(group.name()); + const auto& rate_ix = satellite_inj.rateIndex(ph.value()); + if (rate_ix.has_value()) { + const auto& satrates = satellite_inj[*rate_ix]; + if (!res_rates) { // surface rates + if (const auto& qs = satrates.surface(); qs.has_value()) { + rate = *qs; + } + // We don't support reservoir rates for satellite injection groups + } + } + } + } + return rate; +} + +template +Scalar +WellGroupHelper:: +satelliteProductionRate_(const ScheduleState& sched, + const Group& group, + const GSatProd::GSatProdGroupProp::Rate rate_comp, + bool res_rates) const +{ + Scalar rate = 0.0; + if (group.hasSatelliteProduction()) { + const auto& gsat_prod = sched.gsatprod(); + if (!res_rates) { + rate = gsat_prod.get(group.name(), this->summary_state_).rate[rate_comp]; + } + // We don't support reservoir rates for satellite production groups + } + return rate; +} + +template +std::optional +WellGroupHelper:: +selectRateComponent_(const int phase_pos) const +{ + // TODO: this function can be wrong, phase_pos is not used anymore, this function requries checking and refactoring. + const auto& pu = this->phase_usage_info_; + using Rate = GSatProd::GSatProdGroupProp::Rate; + + for (const auto& [phase, rate_comp] : std::array { + std::pair { IndexTraits::waterPhaseIdx, Rate::Water }, + std::pair { IndexTraits::oilPhaseIdx, Rate::Oil }, + std::pair { IndexTraits::gasPhaseIdx, Rate::Gas } }) + { + if (pu.phaseIsActive(phase)) { + const auto active_phase_pos = pu.canonicalToActivePhaseIdx(phase); + if (active_phase_pos == phase_pos) { + return rate_comp; + } + } + } + + return std::nullopt; +} + +template +int WellGroupHelper:: +updateGroupControlledWellsRecursive_( + const std::string& group_name, + const bool is_production_group, + const Phase injection_phase +) +{ + const Group& group = this->schedule_.getGroup(group_name, this->report_step_); + int num_wells = 0; + for (const std::string& child_group : group.groups()) { + + bool included = false; + if (is_production_group) { + const auto ctrl = this->groupState().production_control(child_group); + included = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE); + } else { + const auto ctrl = this->groupState().injection_control(child_group, injection_phase); + included = included || (ctrl == Group::InjectionCMode::FLD) + || (ctrl == Group::InjectionCMode::NONE); + } + + if (included) { + num_wells += this->updateGroupControlledWellsRecursive_( + child_group, is_production_group, injection_phase); + } else { + this->updateGroupControlledWellsRecursive_(child_group, is_production_group, injection_phase); + } + } + for (const std::string& child_well : group.wells()) { + bool included = false; + const Well& well = this->schedule_.getWell(child_well, this->report_step_); + if (is_production_group && well.isProducer()) { + included = included || this->wellState().isProductionGrup(child_well) || group.as_choke(); + } else if (!is_production_group && !well.isProducer()) { + const auto& well_controls = well.injectionControls(this->summary_state_); + auto injectorType = well_controls.injector_type; + if ( (injection_phase == Phase::WATER && injectorType == InjectorType::WATER ) || + (injection_phase == Phase::OIL && injectorType == InjectorType::OIL ) || + (injection_phase == Phase::GAS && injectorType == InjectorType::GAS )) + { + included = included || this->wellState().isInjectionGrup(child_well); + } + } + const auto ctrl1 = this->groupState().production_control(group.name()); + if (group.as_choke() && ((ctrl1 == Group::ProductionCMode::FLD) || + (ctrl1 == Group::ProductionCMode::NONE))) { + // The auto choke group has not own group control but inherits control from an ancestor group. + // Number of wells should be calculated as zero when wells of auto choke group do not deliver target. + // This behaviour is then similar to no-autochoke group with wells not on GRUP control. + // The rates of these wells are summed up. The parent group target is reduced with this rate. + // This reduced target becomes the target of the other child group of this parent. + const auto num_phases = this->wellState().numPhases(); + std::vector rates(num_phases, 0.0); + for (int phase_pos = 0; phase_pos < num_phases; ++phase_pos) { + rates[phase_pos] = this->sumWellSurfaceRates(group, phase_pos, /*injector=*/false); + } + + // Get the ancestor of the auto choke group that has group control (cmode != FLD, NONE) + const auto& control_group_name = this->controlGroup_(group); + const auto& control_group = this->schedule_.getGroup(control_group_name, this->report_step_); + const auto& ctrl = control_group.productionControls(this->summary_state_); + const auto& control_group_cmode = ctrl.cmode; + + const auto& group_guide_rate = group.productionControls(this->summary_state_).guide_rate; + + if (group_guide_rate > 0) { + // Guide rate is not default for the auto choke group + Scalar grat_target_from_sales = 0.0; + if (this->groupState().has_grat_sales_target(control_group_name)) + grat_target_from_sales = this->groupState().grat_sales_target(control_group_name); + + std::vector resv_coeff(num_phases, 1.0); + WGHelpers::TargetCalculator tcalc{ + control_group_cmode, + this->phase_usage_info_, + resv_coeff, + grat_target_from_sales, + group.name(), + this->groupState(), + group.has_gpmaint_control(control_group_cmode) + }; + const auto& control_group_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + + // Calculates the guide rate of the parent group with control. + // It is allowed that the guide rate of this group is defaulted. The guide rate will be derived from the children groups + const auto& control_group_guide_rate = this->getGuideRate( + control_group_name, tcalc.guideTargetMode() + ); + + if (control_group_guide_rate > 0) { + // Target rate for the auto choke group + const Scalar target_rate = control_group_target * group_guide_rate / control_group_guide_rate; + const Scalar current_rate = tcalc.calcModeRateFromRates(rates); + + if (current_rate < target_rate) + included = false; + } + } + } + + if (included) { + ++num_wells; + } + } + if (is_production_group) { + this->groupState().update_number_of_wells_under_group_control(group_name, num_wells); + } else { + this->groupState().update_number_of_wells_under_inj_group_control( + group_name, injection_phase, num_wells + ); + } + + return num_wells; +} + +template +void WellGroupHelper:: +updateGroupTargetReductionRecursive_( + const Group& group, const bool is_injector, std::vector& group_target_reduction) +{ + const int np = this->wellState().numPhases(); + for (const std::string& sub_group_name : group.groups()) { + std::vector sub_group_target_reduction(np, 0.0); + const Group& sub_group = this->schedule_.getGroup(sub_group_name, this->report_step_); + this->updateGroupTargetReductionRecursive_(sub_group, is_injector, sub_group_target_reduction); + + const Scalar sub_group_efficiency = sub_group.getGroupEfficiencyFactor(); + + // accumulate group contribution from sub group + if (is_injector) { + const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; + for (Phase phase : all) { + const auto phase_pos = this->phaseToActivePhaseIdx_(phase); + // the phase is not present + if (phase_pos == -1) + continue; + const Group::InjectionCMode& current_group_control + = this->groupState().injection_control(sub_group.name(), phase); + const bool individual_control = (current_group_control != Group::InjectionCMode::FLD + && current_group_control != Group::InjectionCMode::NONE); + const int num_group_controlled_wells + = this->groupControlledWells(sub_group.name(), "", !is_injector, phase); + if (individual_control || num_group_controlled_wells == 0) { + group_target_reduction[phase_pos] += sub_group_efficiency * + this->sumWellSurfaceRates(sub_group, phase_pos, is_injector); + } + else { + // Accumulate from this subgroup only if no group guide rate is set for it. + if (!this->guide_rate_.has(sub_group.name(), phase)) { + group_target_reduction[phase_pos] += sub_group_efficiency * + sub_group_target_reduction[phase_pos]; + } + } + } + } + else { + const Group::ProductionCMode& current_group_control = + this->groupState().production_control(sub_group.name()); + const bool individual_control = (current_group_control != Group::ProductionCMode::FLD + && current_group_control != Group::ProductionCMode::NONE); + const int num_group_controlled_wells + = this->groupControlledWells( + sub_group.name(), + /*always_included_child=*/"", + !is_injector, + /*injection_phase_not_used=*/Phase::OIL + ); + if (individual_control || num_group_controlled_wells == 0) { + for (int phase = 0; phase < np; phase++) { + group_target_reduction[phase] + += sub_group_efficiency * this->sumWellSurfaceRates(sub_group, phase, is_injector); + } + } + else { + // The subgroup may participate in group control. + if (!this->guide_rate_.has(sub_group.name())) { + // Accumulate from this subgroup only if no group guide rate is set for it. + for (int phase = 0; phase < np; phase++) { + group_target_reduction[phase] += sub_group_efficiency * + sub_group_target_reduction[phase]; + } + } + } + } + } + + for (const std::string& well_name : group.wells()) { + const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_); + + if (well_tmp.isProducer() && is_injector) + continue; + + if (well_tmp.isInjector() && !is_injector) + continue; + + const auto well_index = this->wellState().index(well_name); + if (!well_index.has_value()) + continue; + + if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + { + continue; + } + + const auto& ws = this->wellState().well(well_index.value()); + if (ws.status == Well::Status::SHUT) + continue; + + const Scalar efficiency = well_tmp.getEfficiencyFactor() * + this->wellState().well(well_index.value()).efficiency_scaling_factor; + + // add contribution from wells not under group control + if (is_injector) { + if (ws.injection_cmode != Well::InjectorCMode::GRUP) + for (int phase = 0; phase < np; phase++) { + group_target_reduction[phase] += ws.surface_rates[phase] * efficiency; + } + } + else { + if ((ws.production_cmode != Well::ProducerCMode::GRUP)){ + if (!group.as_choke()) { + for (int phase = 0; phase < np; phase++) { + group_target_reduction[phase] -= ws.surface_rates[phase] * efficiency; + } + } + } + } + } + if (is_injector) { + this->groupState().update_injection_reduction_rates(group.name(), group_target_reduction); + } + else { + this->groupState().update_production_reduction_rates(group.name(), group_target_reduction); + } +} + + +template class WellGroupHelper; +#ifdef HAVE_FLOAT +template class WellGroupHelper; +#endif +} // namespace Opm diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp new file mode 100644 index 0000000000..6f3c47325e --- /dev/null +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -0,0 +1,386 @@ +/* + Copyright 2025 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ +#ifndef OPM_WELLGROUPHELPER_HEADER_INCLUDED +#define OPM_WELLGROUPHELPER_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace Opm { + +template +class WellGroupHelper { +public: + // RAII guard for temporarily setting wellstate pointer + class WellStateGuard { + public: + WellStateGuard(WellGroupHelper& wgHelper, WellState& well_state) + : wgHelper_{wgHelper} + , previous_state_ptr_{wgHelper_.well_state_} + { + // Set the new state directly + wgHelper_.well_state_ = &well_state; + } + + ~WellStateGuard() { + // Restore the previous state + wgHelper_.well_state_ = previous_state_ptr_; + } + + // Delete copy and move operations + WellStateGuard(const WellStateGuard&) = delete; + WellStateGuard& operator=(const WellStateGuard&) = delete; + WellStateGuard(WellStateGuard&&) = delete; + WellStateGuard& operator=(WellStateGuard&&) = delete; + + private: + WellGroupHelper& wgHelper_; + WellState* previous_state_ptr_; + }; + + // RAII guard for temporarily setting groupstate pointer + class GroupStateGuard { + public: + GroupStateGuard(WellGroupHelper& wgHelper, GroupState& group_state) + : wgHelper_{wgHelper} + , previous_state_ptr_{wgHelper_.group_state_} + { + // Set the new state directly + wgHelper_.group_state_ = &group_state; + } + + ~GroupStateGuard() { + // Restore the previous state + wgHelper_.group_state_ = previous_state_ptr_; + } + + // Delete copy and move operations + GroupStateGuard(const GroupStateGuard&) = delete; + GroupStateGuard& operator=(const GroupStateGuard&) = delete; + GroupStateGuard(GroupStateGuard&&) = delete; + GroupStateGuard& operator=(GroupStateGuard&&) = delete; + + private: + WellGroupHelper& wgHelper_; + GroupState* previous_state_ptr_; + }; + + WellGroupHelper( + const Schedule& schedule, + const SummaryState& summary_state, + const GuideRate& guide_rate, + const PhaseUsageInfo& phase_usage_info + ); + + void accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor); + std::pair checkGroupConstraintsInj( + const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate + ) const; + std::pair checkGroupConstraintsProd( + const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate + ) const; + std::map computeNetworkPressures( + const Network::ExtNetwork& network, + const VFPProdProperties& vfp_prod_props, + const Parallel::Communication& comm + ) const; + DeferredLogger& deferredLogger() const { return *this->deferred_logger_; } + Scalar getGuideRate(const std::string& name, const GuideRateModel::Target target) const; + GuideRate::RateVector getProductionGroupRateVector(const std::string& group_name) const; + Scalar getWellGroupTargetInjector( + const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff + ); + Scalar getWellGroupTargetProducer( + const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff + ); + GuideRate::RateVector getWellRateVector(const std::string& name) const; + std::vector groupChainTopBot(const std::string& bottom, const std::string& top) const; + int groupControlledWells( + const std::string& group_name, + const std::string& always_included_child, + const bool is_production_group, + const Phase injection_phase + ) const; + const GroupState& groupState() const { return *this->group_state_; } + GroupState& groupState() { return *this->group_state_; } + const PhaseUsageInfo& phaseUsageInfo() const { return this->phase_usage_info_; } + GroupStateGuard pushGroupState(GroupState& group_state) { + return GroupStateGuard(*this, group_state); + } + WellStateGuard pushWellState(WellState& well_state) { + return WellStateGuard(*this, well_state); + } + void setCmodeGroup(const Group& group); + void setLogger(DeferredLogger* deferred_logger) { deferred_logger_ = deferred_logger; } + template + void setRegionAveragePressureCalculator( + const Group& group, + const FieldPropsManager& fp, + std::map>& regional_average_pressure_calculator + ); + Scalar sumSolventRates(const Group& group, const bool is_injector); + Scalar sumWellResRates(const Group& group, const int phase_pos, const bool injector) const; + Scalar sumWellSurfaceRates(const Group& group, const int phase_pos, const bool injector) const; + Scalar sumWellPhaseRates( + bool res_rates, + const Group& group, + const int phase_pos, + const bool injector, + const bool network = false + ) const; + template void updateGpMaintTargetForGroups( + const Group& group, const RegionalValues& regional_values, const double dt + ); + int updateGroupControlledWells(const bool is_production_group, const Phase injection_phase); + void updateGroupProductionRates(const Group& group); + void updateGroupTargetReduction(const Group& group, const bool is_injector); + void updateNetworkLeafNodeProductionRates(); + void updateREINForGroups(const Group& group, const bool sum_rank); + void updateReservoirRatesInjectionGroups(const Group& group); + void updateVREPForGroups(const Group& group); + void updateState( + WellState& well_state, GroupState& group_state, int report_step + ); + void updateSurfaceRatesInjectionGroups(const Group& group); + void updateWellRates(const Group& group, const WellState& well_state_nupcol); + const WellState& wellState() const { return *this->well_state_; } + WellState& wellState() { return *this->well_state_; } + void updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool is_injector); + std::pair, Scalar> worstOffendingWell( + const Group& group, + const Group::ProductionCMode& offended_control, + const Parallel::Communication& comm + ) const; +private: + std::string controlGroup_(const Group& group); + GuideRate::RateVector getGuideRateVector_(const std::vector& rates) const; + bool isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const; + int phaseToActivePhaseIdx_(const Phase phase); + Scalar satelliteInjectionRate_( + const ScheduleState& sched, + const Group& group, + const int phase_pos, + bool res_rates + ) const; + Scalar satelliteProductionRate_( + const ScheduleState& sched, + const Group& group, + const GSatProd::GSatProdGroupProp::Rate rate_comp, + bool res_rates + ) const; + std::optional selectRateComponent_(const int phase_pos) const; + int updateGroupControlledWellsRecursive_( + const std::string& group_name, const bool is_production_group, const Phase injection_phase); + void updateGroupTargetReductionRecursive_( + const Group& group, const bool is_injector, std::vector& group_target_reduction); + + const Schedule& schedule_; + const SummaryState& summary_state_; + WellState* well_state_; + GroupState* group_state_{nullptr}; + const GuideRate& guide_rate_; + int report_step_{0}; + DeferredLogger* deferred_logger_{nullptr}; + // NOTE: The phase usage info seems to be read-only throughout the simulation, so it should be safe + // to store a reference to it here. + const PhaseUsageInfo& phase_usage_info_; +}; + +// ----------------------------------------------------------------------------- +// Template member function implementations +// ----------------------------------------------------------------------------- + +// NOTE: This template member function is defined here in the header because the +// AverageRegionalPressureType type parameter depends on derived class types that are +// not available when WellGroupHelper.cpp is compiled. Template functions +// must be visible at their instantiation point. +// See WellGroupHelper.cpp for detailed rationale. +template +template +void WellGroupHelper:: +setRegionAveragePressureCalculator( + const Group& group, + const FieldPropsManager& fp, + std::map>& regional_average_pressure_calculator) +{ + for (const std::string& groupName : group.groups()) { + this->setRegionAveragePressureCalculator(this->schedule_.getGroup(groupName, this->report_step_), fp, regional_average_pressure_calculator); + } + const auto& gpm = group.gpmaint(); + if (!gpm) + return; + + const auto& reg = gpm->region(); + if (!reg) + return; + + if (regional_average_pressure_calculator.count(reg->first) == 0) { + const std::string name = (reg->first.rfind("FIP", 0) == 0) ? reg->first : "FIP" + reg->first; + const auto& fipnum = fp.get_int(name); + regional_average_pressure_calculator[reg->first] = + std::make_unique(fipnum); + } +} + +// NOTE: This template member function is defined in the header because the +// RegionalValues type parameter depends on derived class types that are +// not available when WellGroupHelper.cpp is compiled. Template functions +// must be visible at their instantiation point. +// See WellGroupHelper.cpp for detailed rationale. +template +template +void WellGroupHelper:: +updateGpMaintTargetForGroups(const Group& group, + const RegionalValues& regional_values, + const double dt) +{ + OPM_TIMEFUNCTION(); + for (const std::string& group_name : group.groups()) { + const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); + this->updateGpMaintTargetForGroups(group_tmp, regional_values, dt); + } + const auto& gpm = group.gpmaint(); + if (!gpm) + return; + + const auto& region = gpm->region(); + if (!region) + return; + + const auto [name, number] = *region; + const Scalar error = gpm->pressure_target() - regional_values.at(name)->pressure(number); + Scalar current_rate = 0.0; + const auto& pu = this->phase_usage_info_; + bool injection = true; + Scalar sign = 1.0; + switch (gpm->flow_target()) { + case GPMaint::FlowTarget::RESV_PROD: + { + current_rate = -this->groupState().injection_vrep_rate(group.name()); + injection = false; + sign = -1.0; + break; + } + case GPMaint::FlowTarget::RESV_OINJ: + { + if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { + const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + current_rate = this->groupState().injection_reservoir_rates(group.name())[io]; + } + break; + } + case GPMaint::FlowTarget::RESV_WINJ: + { + if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { + const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + current_rate = this->groupState().injection_reservoir_rates(group.name())[iw]; + } + break; + } + case GPMaint::FlowTarget::RESV_GINJ: + { + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + current_rate = this->groupState().injection_reservoir_rates(group.name())[ig]; + } + break; + } + case GPMaint::FlowTarget::SURF_OINJ: + { + if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { + const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + current_rate = this->groupState().injection_surface_rates(group.name())[io]; + } + break; + } + case GPMaint::FlowTarget::SURF_WINJ: + { + if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { + const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + current_rate = this->groupState().injection_surface_rates(group.name())[iw]; + } + break; + } + case GPMaint::FlowTarget::SURF_GINJ: + { + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + current_rate = this->groupState().injection_surface_rates(group.name())[ig]; + } + break; + } + default: + throw std::invalid_argument("Invalid Flow target type in GPMAINT"); + } + auto& gpmaint_state = this->groupState().gpmaint(group.name()); + // we only activate gpmaint if pressure is lower than the target regional pressure for injectors + // (i.e. error > 0) and higher for producers. + bool activate = (injection && error > 0) || (!injection && error < 0); + Scalar rate = 0.0; + if (activate) { + rate = gpm->rate(gpmaint_state, current_rate, error, dt); + } else { + gpm->resetState(gpmaint_state); + } + this->groupState().update_gpmaint_target(group.name(), std::max(Scalar{0.0}, sign * rate)); +} + +} // namespace Opm + +#endif diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index 0279704534..1b30dbe4d5 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -1437,682 +1437,6 @@ control_group(const Group& group, return group.name(); } -template -std::pair -WellGroupHelpers:: -checkGroupConstraintsProd(const std::string& name, - const std::string& parent, - const Group& group, - const WellState& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - const bool check_guide_rate, - DeferredLogger& deferred_logger) -{ - // When called for a well ('name' is a well name), 'parent' - // will be the name of 'group'. But if we recurse, 'name' and - // 'parent' will stay fixed while 'group' will be higher up - // in the group tree. - // efficiencyfactor is the well efficiency factor for the first group the well is - // part of. Later it is the accumulated factor including the group efficiency factor - // of the child of group. - - // NOTE: if name is a group, the rates-argument is the portion of the 'full' group rates that - // is potentially available for control by an ancestor, i.e. full rates minus reduction rates - OPM_TIMEFUNCTION(); - const Group::ProductionCMode& currentGroupControl = group_state.production_control(group.name()); - - if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) { - // Return if we are not available for parent group. - if (!group.productionGroupControlAvailable()) { - return std::make_pair(false, 1); - } - // Otherwise: check production share of parent's control. - const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx); - return checkGroupConstraintsProd(name, - parent, - parentGroup, - wellState, - group_state, - reportStepIdx, - guideRate, - rates, - efficiencyFactor * group.getGroupEfficiencyFactor(), - schedule, - summaryState, - resv_coeff, - check_guide_rate, - deferred_logger); - } - - // This can be false for FLD-controlled groups, we must therefore - // check for FLD first (done above). - if (!group.isProductionGroup()) { - return std::make_pair(false, Scalar(1.0)); - } - - // If we are here, we are at the topmost group to be visited in the recursion. - // This is the group containing the control we will check against. - - // gconsale may adjust the grat target. - // the adjusted rates is send to the targetCalculator - Scalar gratTargetFromSales = 0.0; - if (group_state.has_grat_sales_target(group.name())) - gratTargetFromSales = group_state.grat_sales_target(group.name()); - - WGHelpers::TargetCalculator tcalc(currentGroupControl, - wellState.phaseUsageInfo(), - resv_coeff, - gratTargetFromSales, - group.name(), - group_state, - group.has_gpmaint_control(currentGroupControl)); - - WGHelpers::FractionCalculator fcalc(schedule, - wellState, - group_state, - summaryState, - reportStepIdx, - guideRate, - tcalc.guideTargetMode(), - true, - Phase::OIL); - - auto localFraction = [&](const std::string& child) { return fcalc.localFraction(child, name); }; - - auto localReduction = [&](const std::string& group_name) - { - const std::vector& groupTargetReductions = - group_state.production_reduction_rates(group_name); - return tcalc.calcModeRateFromRates(groupTargetReductions); - }; - - auto localCurrentRate = [&](const std::string& group_name) - { - const std::vector& groupSurfaceRates = - group_state.production_rates(group_name); - return tcalc.calcModeRateFromRates(groupSurfaceRates); - }; - - std::optional ctrl; - if (!group.has_gpmaint_control(currentGroupControl)) - ctrl = group.productionControls(summaryState); - - const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); - // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. - // Then ... - // TODO finish explanation. - const Scalar current_rate_available - = -tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. - const auto chain = groupChainTopBot(name, group.name(), schedule, reportStepIdx); - // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. - const std::size_t num_ancestors = chain.size() - 1; - - // check whether guide rate is violated - if (check_guide_rate) { - for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - if (guideRate->has(chain[ii])) { - const auto& guided_group = chain[ii]; - const Scalar grefficiency - = schedule.getGroup(guided_group, reportStepIdx).getGroupEfficiencyFactor(); - const Scalar currentRateFraction = grefficiency * localCurrentRate(guided_group) / (localCurrentRate(chain[ii-1])); - const Scalar guiderateFraction = localFraction(guided_group); - // we add a factor here to avoid switching due to numerical instability - const Scalar factor = 1.01; - if (currentRateFraction > (guiderateFraction * factor)) { - return std::make_pair(true, guiderateFraction / currentRateFraction); - } - } - } - } - - if (schedule.hasWell(name) && wellState.well(name).group_target) { // for wells we already have computed the target - const Scalar current_well_rate_available = -tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. - const Scalar group_target_rate_available = *wellState.well(name).group_target; - Scalar scale = 1.0; - if (current_well_rate_available > 1e-12) { - scale = group_target_rate_available / current_well_rate_available; - } - - return std::make_pair(current_well_rate_available > group_target_rate_available, scale); - } - - // we need to find out the level where the current well is applied to the local reduction - std::size_t local_reduction_level = 0; - for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = groupControlledWells(schedule, - wellState, - group_state, - reportStepIdx, - chain[ii], - "", - /*is_producer*/ true, - /*injectionPhaseNotUsed*/ Phase::OIL); - if (guideRate->has(chain[ii]) && num_gr_ctrl > 0) { - local_reduction_level = ii; - } - } - - // Compute portion of target corresponding to current_rate_available - Scalar target = orig_target; - for (std::size_t ii = 0; ii < num_ancestors; ++ii) { - if ((ii == 0) || guideRate->has(chain[ii])) { - // Apply local reductions only at the control level - // (top) and for levels where we have a specified - // group guide rate. - if (local_reduction_level >= ii ) { - target -= localReduction(chain[ii]); - } - // Add my reduction back at the level where it is included in the local reduction - if (local_reduction_level == ii ) { - target += current_rate_available * efficiencyFactor; - } - } - target *= localFraction(chain[ii + 1]); - } - // Avoid negative target rates comming from too large local reductions. - const Scalar target_rate_available = std::max(Scalar(1e-12), target / efficiencyFactor); - - Scalar scale = 1.0; - if (current_rate_available > 1e-12) - scale = target_rate_available / current_rate_available; - - return std::make_pair(current_rate_available > target_rate_available, scale); -} - - -template -Scalar -WellGroupHelpers:: -getWellGroupTargetProducer(const std::string& name, - const std::string& parent, - const Group& group, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - DeferredLogger& deferred_logger) -{ - // This function computes a wells group target. - // 'parent' will be the name of 'group'. But if we recurse, 'name' and - // 'parent' will stay fixed while 'group' will be higher up - // in the group tree. - // Eficiencyfactor is the well efficiency factor for the first group the well is - // part of. Later it is the accumulated factor including the group efficiency factor - // of the child of group. - OPM_TIMEFUNCTION(); - const Group::ProductionCMode& currentGroupControl = group_state.production_control(group.name()); - - if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) { - // Return if we are not available for parent group. - if (!group.productionGroupControlAvailable()) { - return std::numeric_limits::max(); - } - // Otherwise: check production share of parent's control. - const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx); - return getWellGroupTargetProducer(name, - parent, - parentGroup, - wellState, - group_state, - reportStepIdx, - guideRate, - rates, - efficiencyFactor * group.getGroupEfficiencyFactor(), - schedule, - summaryState, - resv_coeff, - deferred_logger); - } - - // This can be false for FLD-controlled groups, we must therefore - // check for FLD first (done above). - if (!group.isProductionGroup()) { - return std::numeric_limits::max(); - } - - // If we are here, we are at the topmost group to be visited in the recursion. - // This is the group containing the control we will check against. - - // gconsale may adjust the grat target. - // the adjusted rates is send to the targetCalculator - Scalar gratTargetFromSales = 0.0; - if (group_state.has_grat_sales_target(group.name())) - gratTargetFromSales = group_state.grat_sales_target(group.name()); - - WGHelpers::TargetCalculator tcalc(currentGroupControl, - wellState.phaseUsageInfo(), - resv_coeff, - gratTargetFromSales, - group.name(), - group_state, - group.has_gpmaint_control(currentGroupControl)); - - WGHelpers::FractionCalculator fcalc(schedule, - wellState, - group_state, - summaryState, - reportStepIdx, - guideRate, - tcalc.guideTargetMode(), - true, - Phase::OIL); - auto localFraction = [&](const std::string& child, const std::string& always_incluced_name) { return fcalc.localFraction(child, always_incluced_name); }; - - auto localReduction = [&](const std::string& group_name) - { - const std::vector& groupTargetReductions = - group_state.production_reduction_rates(group_name); - return tcalc.calcModeRateFromRates(groupTargetReductions); - }; - - std::optional ctrl; - if (!group.has_gpmaint_control(currentGroupControl)) - ctrl = group.productionControls(summaryState); - - const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); - // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. - // Then ... - // TODO finish explanation. - const Scalar current_rate_available - = -tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. - const auto chain = groupChainTopBot(name, group.name(), schedule, reportStepIdx); - // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. - const std::size_t num_ancestors = chain.size() - 1; - // we need to find out the level where the local reduction is applied - std::size_t local_reduction_level = 0; - for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = groupControlledWells(schedule, - wellState, - group_state, - reportStepIdx, - chain[ii], - "", - /*is_producer*/ true, - /*injectionPhaseNotUsed*/ Phase::OIL); - if (guideRate->has(chain[ii]) && num_gr_ctrl > 0) { - local_reduction_level = ii; - } - } - // Compute portion of target corresponding to current_rate_available - Scalar target = orig_target; - for (std::size_t ii = 0; ii < num_ancestors; ++ii) { - if ((ii == 0) || guideRate->has(chain[ii])) { - // Apply local reductions only at the control level - // (top) and for levels where we have a specified - // group guide rate. - if (local_reduction_level >= ii) { - target -= localReduction(chain[ii]); - } - // If we are under individual control we need to add the wells rate back at the level where it is included in the local reduction - if (local_reduction_level == ii && !wellState.isProductionGrup(name)) { - target += current_rate_available * efficiencyFactor; - } - } - if (wellState.isProductionGrup(name)) { - target *= localFraction(chain[ii + 1], chain[ii + 1]); - } else { - target *= localFraction(chain[ii + 1], name); - } - } - // Avoid negative target rates comming from too large local reductions. - return std::max(Scalar(0.0), target / efficiencyFactor);; -} - -template -std::pair -WellGroupHelpers:: -checkGroupConstraintsInj(const std::string& name, - const std::string& parent, - const Group& group, - const WellState& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - Phase injectionPhase, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - const bool check_guide_rate, - DeferredLogger& deferred_logger) -{ - // When called for a well ('name' is a well name), 'parent' - // will be the name of 'group'. But if we recurse, 'name' and - // 'parent' will stay fixed while 'group' will be higher up - // in the group tree. - // efficiencyfactor is the well efficiency factor for the first group the well is - // part of. Later it is the accumulated factor including the group efficiency factor - // of the child of group. - - // NOTE: if name is a group, the rates-argument is the portion of the 'full' group rates that - // is potentially available for control by an ancestor, i.e. full rates minus reduction rates - - auto currentGroupControl = group_state.injection_control(group.name(), injectionPhase); - - if (currentGroupControl == Group::InjectionCMode::FLD || currentGroupControl == Group::InjectionCMode::NONE) { - // Return if we are not available for parent group. - if (!group.injectionGroupControlAvailable(injectionPhase)) { - return std::make_pair(false, Scalar(1)); - } - // Otherwise: check production share of parent's control. - const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx); - return checkGroupConstraintsInj(name, - parent, - parentGroup, - wellState, - group_state, - reportStepIdx, - guideRate, - rates, - injectionPhase, - efficiencyFactor * group.getGroupEfficiencyFactor(), - schedule, - summaryState, - resv_coeff, - check_guide_rate, - deferred_logger); - } - - // This can be false for FLD-controlled groups, we must therefore - // check for FLD first (done above). - if (!group.isInjectionGroup()) { - return std::make_pair(false, Scalar(1.0)); - } - - // If we are here, we are at the topmost group to be visited in the recursion. - // This is the group containing the control we will check against. - Scalar sales_target = 0; - if (schedule[reportStepIdx].gconsale().has(group.name())) { - const auto& gconsale = schedule[reportStepIdx].gconsale().get(group.name(), summaryState); - sales_target = gconsale.sales_target; - } - WGHelpers::InjectionTargetCalculator tcalc(currentGroupControl, - wellState.phaseUsageInfo(), - resv_coeff, - group.name(), - sales_target, - group_state, - injectionPhase, - group.has_gpmaint_control(injectionPhase, - currentGroupControl), - deferred_logger); - - WGHelpers::FractionCalculator fcalc(schedule, - wellState, - group_state, - summaryState, - reportStepIdx, - guideRate, - tcalc.guideTargetMode(), - false, - injectionPhase); - - auto localFraction = [&](const std::string& child) { return fcalc.localFraction(child, name); }; - - auto localReduction = [&](const std::string& group_name) - { - const std::vector& groupTargetReductions = - group_state.injection_reduction_rates(group_name); - return tcalc.calcModeRateFromRates(groupTargetReductions); - }; - - auto localCurrentRate = [&](const std::string& group_name) - { - const std::vector& groupSurfaceRates = - group_state.injection_surface_rates(group_name); - return tcalc.calcModeRateFromRates(groupSurfaceRates); - }; - - const auto chain = groupChainTopBot(name, group.name(), schedule, reportStepIdx); - // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. - const std::size_t num_ancestors = chain.size() - 1; - // we need to find out the level where the current well is applied to the local reduction - std::size_t local_reduction_level = 0; - for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = groupControlledWells(schedule, - wellState, - group_state, - reportStepIdx, - chain[ii], - "", - /*is_producer*/ false, - injectionPhase); - if (guideRate->has(chain[ii], injectionPhase) && num_gr_ctrl > 0) { - local_reduction_level = ii; - } - } - - // check whether guide rate is violated - if (check_guide_rate) { - for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - if (guideRate->has(chain[ii], injectionPhase)) { - const auto& guided_group = chain[ii]; - const Scalar grefficiency - = schedule.getGroup(guided_group, reportStepIdx).getGroupEfficiencyFactor(); - const Scalar currentRateFraction = grefficiency * localCurrentRate(guided_group) / - localCurrentRate(chain[ii-1]); - const Scalar guiderateFraction = localFraction(guided_group); - // we add a factor here to avoid switching due to numerical instability - const Scalar factor = 1.01; - if (currentRateFraction > (guiderateFraction * factor)) { - return std::make_pair(true, guiderateFraction / currentRateFraction); - } - } - } - } - - if (schedule.hasWell(name) && wellState.well(name).group_target) { // for wells we already have computed the target - Scalar scale = 1.0; - const Scalar group_target_rate_available = *wellState.well(name).group_target; - const Scalar current_well_rate_available = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. - if (current_well_rate_available > 1e-12) { - scale = group_target_rate_available / current_well_rate_available; - } - return std::make_pair(current_well_rate_available > group_target_rate_available, scale); - } - - std::optional ctrl; - if (!group.has_gpmaint_control(injectionPhase, currentGroupControl)) - ctrl = group.injectionControls(injectionPhase, summaryState); - - - const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); - // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. - // Then ... - // TODO finish explanation. - const Scalar current_rate_available - = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. - - // Compute portion of target corresponding to current_rate_available - Scalar target = orig_target; - for (std::size_t ii = 0; ii < num_ancestors; ++ii) { - if ((ii == 0) || guideRate->has(chain[ii], injectionPhase)) { - // Apply local reductions only at the control level - // (top) and for levels where we have a specified - // group guide rate. - if (local_reduction_level >= ii ) { - target -= localReduction(chain[ii]); - } - - // Add my reduction back at the level where it is included in the local reduction - if (local_reduction_level == ii ) { - target += current_rate_available * efficiencyFactor; - } - } - target *= localFraction(chain[ii + 1]); - } - // Avoid negative target rates comming from too large local reductions. - const Scalar target_rate_available = std::max(Scalar(1e-12), target / efficiencyFactor); - Scalar scale = 1.0; - if (current_rate_available > 1e-12) - scale = target_rate_available / current_rate_available; - - return std::make_pair(current_rate_available > target_rate_available, scale); -} - - -template -Scalar -WellGroupHelpers:: -getWellGroupTargetInjector(const std::string& name, - const std::string& parent, - const Group& group, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - Phase injectionPhase, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - DeferredLogger& deferred_logger) -{ - // This function computes a wells group target. - // 'parent' will be the name of 'group'. But if we recurse, 'name' and - // 'parent' will stay fixed while 'group' will be higher up - // in the group tree. - // Eficiencyfactor is the well efficiency factor for the first group the well is - // part of. Later it is the accumulated factor including the group efficiency factor - // of the child of group. - auto currentGroupControl = group_state.injection_control(group.name(), injectionPhase); - if (currentGroupControl == Group::InjectionCMode::FLD || currentGroupControl == Group::InjectionCMode::NONE) { - // Return if we are not available for parent group. - if (!group.injectionGroupControlAvailable(injectionPhase)) { - return std::numeric_limits::max(); - } - // Otherwise: check production share of parent's control. - const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx); - return getWellGroupTargetInjector(name, - parent, - parentGroup, - wellState, - group_state, - reportStepIdx, - guideRate, - rates, - injectionPhase, - efficiencyFactor * group.getGroupEfficiencyFactor(), - schedule, - summaryState, - resv_coeff, - deferred_logger); - } - - // This can be false for FLD-controlled groups, we must therefore - // check for FLD first (done above). - if (!group.isInjectionGroup()) { - return std::numeric_limits::max(); - } - - // If we are here, we are at the topmost group to be visited in the recursion. - // This is the group containing the control we will check against. - Scalar sales_target = 0; - if (schedule[reportStepIdx].gconsale().has(group.name())) { - const auto& gconsale = schedule[reportStepIdx].gconsale().get(group.name(), summaryState); - sales_target = gconsale.sales_target; - } - WGHelpers::InjectionTargetCalculator tcalc(currentGroupControl, - wellState.phaseUsageInfo(), - resv_coeff, - group.name(), - sales_target, - group_state, - injectionPhase, - group.has_gpmaint_control(injectionPhase, - currentGroupControl), - deferred_logger); - - WGHelpers::FractionCalculator fcalc(schedule, - wellState, - group_state, - summaryState, - reportStepIdx, - guideRate, - tcalc.guideTargetMode(), - false, - injectionPhase); - - auto localFraction = [&](const std::string& child, const std::string& always_incluced_name) { return fcalc.localFraction(child, always_incluced_name); }; - - auto localReduction = [&](const std::string& group_name) - { - const std::vector& groupTargetReductions = - group_state.injection_reduction_rates(group_name); - return tcalc.calcModeRateFromRates(groupTargetReductions); - }; - - std::optional ctrl; - if (!group.has_gpmaint_control(injectionPhase, currentGroupControl)) - ctrl = group.injectionControls(injectionPhase, summaryState); - - - const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); - // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. - // Then ... - // TODO finish explanation. - const Scalar current_rate_available - = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. - const auto chain = groupChainTopBot(name, group.name(), schedule, reportStepIdx); - // Because 'name' is the last of the elements, and not an ancestor, we subtract one below. - const std::size_t num_ancestors = chain.size() - 1; - // we need to find out the level where the local reduction is applied - std::size_t local_reduction_level = 0; - for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = groupControlledWells(schedule, - wellState, - group_state, - reportStepIdx, - chain[ii], - "", - /*is_producer*/ false, - injectionPhase); - if (guideRate->has(chain[ii], injectionPhase) && num_gr_ctrl > 0) { - local_reduction_level = ii; - } - } - - // Compute portion of target corresponding to current_rate_available - Scalar target = orig_target; - for (std::size_t ii = 0; ii < num_ancestors; ++ii) { - if ((ii == 0) || guideRate->has(chain[ii], injectionPhase)) { - // Apply local reductions only at the control level - // (top) and for levels where we have a specified - // group guide rate. - if (local_reduction_level >= ii ) { - target -= localReduction(chain[ii]); - } - - // If we are under individual control we need to add the wells rate back at the level where it is included in the local reduction - if (local_reduction_level == ii && !wellState.isInjectionGrup(name)) { - target += current_rate_available * efficiencyFactor; - } - } - if (!wellState.isInjectionGrup(name)) { - target *= localFraction(chain[ii + 1], name); - } else { - target *= localFraction(chain[ii + 1], chain[ii + 1]); - } - } - // Avoid negative target rates comming from too large local reductions. - return std::max(Scalar(0.0), target / efficiencyFactor); -} - - template std::pair, Scalar> WellGroupHelpers:: diff --git a/opm/simulators/wells/WellGroupHelpers.hpp b/opm/simulators/wells/WellGroupHelpers.hpp index dce1ad4803..399e0a93da 100644 --- a/opm/simulators/wells/WellGroupHelpers.hpp +++ b/opm/simulators/wells/WellGroupHelpers.hpp @@ -255,39 +255,6 @@ public: const bool is_production_group, const Phase injection_phase); - static std::pair - checkGroupConstraintsInj(const std::string& name, - const std::string& parent, - const Group& group, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - Phase injectionPhase, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - const bool check_guide_rate, - DeferredLogger& deferred_logger); - - static Scalar - getWellGroupTargetInjector(const std::string& name, - const std::string& parent, - const Group& group, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - Phase injectionPhase, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - DeferredLogger& deferred_logger); - static std::vector groupChainTopBot(const std::string& bottom, const std::string& top, @@ -308,36 +275,6 @@ public: const int reportStepIdx, const Schedule& schedule); - static std::pair - checkGroupConstraintsProd(const std::string& name, - const std::string& parent, - const Group& group, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - const bool check_guide_rate, - DeferredLogger& deferred_logger); - static Scalar - getWellGroupTargetProducer(const std::string& name, - const std::string& parent, - const Group& group, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const Scalar* rates, - const Scalar efficiencyFactor, - const Schedule& schedule, - const SummaryState& summaryState, - const std::vector& resv_coeff, - DeferredLogger& deferred_logger); - template static void setRegionAveragePressureCalculator(const Group& group, const Schedule& schedule, diff --git a/opm/simulators/wells/WellInterface.hpp b/opm/simulators/wells/WellInterface.hpp index e6cc5d37f0..428a130893 100644 --- a/opm/simulators/wells/WellInterface.hpp +++ b/opm/simulators/wells/WellInterface.hpp @@ -69,6 +69,7 @@ namespace Opm class WellInjectionProperties; class WellProductionProperties; +template class WellGroupHelper; template class WellInterface : public WellInterfaceIndices, @@ -98,6 +99,7 @@ public: using WellStateType = WellState; using SingleWellStateType = SingleWellState; + using WellGroupHelperType = WellGroupHelper; using RateConverterType = typename WellInterfaceFluidSystem::RateConverterType; @@ -166,8 +168,7 @@ public: void assembleWellEq(const Simulator& simulator, const double dt, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); void assembleWellEqWithoutIteration(const Simulator& simulator, @@ -179,8 +180,7 @@ public: // TODO: better name or further refactoring the function to make it more clear void prepareWellBeforeAssembling(const Simulator& simulator, const double dt, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); virtual void computeWellRatesWithBhp(const Simulator& ebosSimulator, @@ -190,6 +190,7 @@ public: virtual std::optional computeBhpAtThpLimitProdWithAlq(const Simulator& ebos_simulator, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, const Scalar alq_value, DeferredLogger& deferred_logger, @@ -211,18 +212,19 @@ public: // TODO: before we decide to put more information under mutable, this function is not const virtual void computeWellPotentials(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) = 0; virtual void updateWellStateWithTarget(const Simulator& simulator, - const GroupState& group_state, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const; virtual void scaleSegmentRatesAndPressure(WellStateType& well_state) const; virtual void computeWellRatesWithBhpIterations(const Simulator& simulator, const Scalar& bhp, + const WellGroupHelperType& wgHelper, std::vector& well_flux, DeferredLogger& deferred_logger) const = 0; @@ -241,18 +243,17 @@ public: bool updateWellStateWithTHPTargetProd(const Simulator& simulator, WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const; enum class IndividualOrGroup { Individual, Group, Both }; bool updateWellControl(const Simulator& simulator, const IndividualOrGroup iog, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) /* const */; bool updateWellControlAndStatusLocalIteration(const Simulator& simulator, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const Scalar WQTotal, @@ -290,8 +291,7 @@ public: // Simulator is not const is because that assembleWellEq is non-const Simulator void wellTesting(const Simulator& simulator, const double simulation_time, - /* const */ WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, WellTestState& welltest_state, GLiftEclWells& ecl_well_map, std::map& open_times, @@ -299,6 +299,7 @@ public: void checkWellOperability(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); void gliftBeginTimeStepWellTestUpdateALQ(const Simulator& simulator, @@ -311,10 +312,12 @@ public: // mostly related to BHP limit and THP limit void updateWellOperability(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); bool updateWellOperabilityFromWellEq(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); // update perforation water throughput based on solved water rate @@ -335,8 +338,7 @@ public: DeferredLogger& deferred_logger) const; void solveWellEquation(const Simulator& simulator, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); const std::vector& connectionRates() const @@ -359,8 +361,7 @@ public: const double dt, const WellInjectionControls& inj_controls, const WellProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false) = 0; @@ -393,6 +394,7 @@ protected: // check whether the well is operable under THP limit with current reservoir condition virtual void checkOperabilityUnderTHPLimit(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) = 0; virtual void updateIPR(const Simulator& simulator, @@ -411,8 +413,7 @@ protected: const double dt, const WellInjectionControls& inj_controls, const WellProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) = 0; virtual void updateIPRImplicit(const Simulator& simulator, @@ -421,39 +422,36 @@ protected: bool iterateWellEquations(const Simulator& simulator, const double dt, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); bool solveWellWithOperabilityCheck(const Simulator& simulator, const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); std::optional estimateOperableBhp(const Simulator& ebos_simulator, const double dt, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger); bool solveWellWithBhp(const Simulator& simulator, const double dt, const Scalar bhp, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); bool solveWellWithZeroRate(const Simulator& simulator, const double dt, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); bool solveWellForTesting(const Simulator& simulator, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index 91878ea45e..b3fddfee13 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include namespace Opm @@ -179,13 +179,15 @@ checkIndividualConstraints(SingleWellState& ws, template bool WellInterfaceFluidSystem:: -checkGroupConstraints(WellState& well_state, - const GroupState& group_state, +checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const bool check_guide_rate, DeferredLogger& deferred_logger) const { + const auto& group_state = wgHelper.groupState(); + + if (!this->wellEcl().isAvailableForGroupControl()) return false; @@ -201,7 +203,7 @@ checkGroupConstraints(WellState& well_state, this->rateConverter().calcInjCoeff(id, region, coeff); }; - return WellGroupConstraints(*this).checkGroupConstraints(well_state, group_state, + return WellGroupConstraints(*this).checkGroupConstraints(wgHelper, schedule, summaryState, rCoeff, check_guide_rate, deferred_logger); } @@ -209,18 +211,18 @@ checkGroupConstraints(WellState& well_state, template bool WellInterfaceFluidSystem:: -checkConstraints(WellState& well_state, - const GroupState& group_state, +checkConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, DeferredLogger& deferred_logger) const { + auto& well_state = const_cast(wgHelper).wellState(); const bool ind_broken = checkIndividualConstraints(well_state.well(this->index_of_well_), summaryState, deferred_logger); if (ind_broken) { return true; } else { - return checkGroupConstraints(well_state, group_state, schedule, + return checkGroupConstraints(wgHelper, schedule, summaryState, true, deferred_logger); } } diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.hpp b/opm/simulators/wells/WellInterfaceFluidSystem.hpp index 061e26fdcc..6f35bf1676 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.hpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.hpp @@ -43,6 +43,7 @@ class Schedule; struct RatioLimitCheckReport; template class SingleWellState; template class WellState; +template class WellGroupHelper; template class WellInterfaceFluidSystem : public WellInterfaceGeneric @@ -57,6 +58,7 @@ public: using Scalar = typename FluidSystem::Scalar; using IndexTraits = typename FluidSystem::IndexTraitsType; using ModelParameters = typename WellInterfaceGeneric::ModelParameters; + using WellGroupHelperType = WellGroupHelper; static constexpr int Water = IndexTraits::waterPhaseIdx; static constexpr int Oil = IndexTraits::oilPhaseIdx; @@ -88,15 +90,13 @@ protected: const std::optional& inj_controls = std::nullopt, const std::optional& prod_controls = std::nullopt) const; - bool checkGroupConstraints(WellState& well_state, - const GroupState& group_state, + bool checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const bool check_guide_rate, DeferredLogger& deferred_logger) const; - bool checkConstraints(WellState& well_state, - const GroupState& group_state, + bool checkConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, DeferredLogger& deferred_logger) const; diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 06d9493607..1c3de2eaf8 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -188,11 +188,11 @@ namespace Opm WellInterface:: updateWellControl(const Simulator& simulator, const IndividualOrGroup iog, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) /* const */ { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); if (stoppedOrZeroRateTarget(simulator, well_state, deferred_logger)) { return false; } @@ -233,10 +233,10 @@ namespace Opm if (iog == IndividualOrGroup::Individual) { changed = this->checkIndividualConstraints(ws, summaryState, deferred_logger); } else if (iog == IndividualOrGroup::Group) { - changed = this->checkGroupConstraints(well_state, group_state, schedule, summaryState, true, deferred_logger); + changed = this->checkGroupConstraints(wgHelper, schedule, summaryState, true, deferred_logger); } else { assert(iog == IndividualOrGroup::Both); - changed = this->checkConstraints(well_state, group_state, schedule, summaryState, deferred_logger); + changed = this->checkConstraints(wgHelper, schedule, summaryState, deferred_logger); } Parallel::Communication cc = simulator.vanguard().grid().comm(); // checking whether control changed @@ -262,7 +262,7 @@ namespace Opm if (iterationIdx >= nupcol || this->well_control_log_.empty()) { this->well_control_log_.push_back(from); } - updateWellStateWithTarget(simulator, group_state, well_state, deferred_logger); + updateWellStateWithTarget(simulator, wgHelper, deferred_logger); updatePrimaryVariables(simulator, well_state, deferred_logger); } @@ -273,8 +273,7 @@ namespace Opm bool WellInterface:: updateWellControlAndStatusLocalIteration(const Simulator& simulator, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const Scalar wqTotal, @@ -283,6 +282,7 @@ namespace Opm const bool fixed_status) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); const auto& summary_state = simulator.vanguard().summaryState(); const auto& schedule = simulator.vanguard().schedule(); auto& ws = well_state.well(this->index_of_well_); @@ -317,7 +317,7 @@ namespace Opm changed = this->checkIndividualConstraints(ws, summary_state, deferred_logger, inj_controls, prod_controls); } if (hasGroupControl && this->param_.check_group_constraints_inner_well_iterations_) { - changed = changed || this->checkGroupConstraints(well_state, group_state, schedule, summary_state, false, deferred_logger); + changed = changed || this->checkGroupConstraints(wgHelper, schedule, summary_state, false, deferred_logger); } if (changed) { @@ -327,7 +327,7 @@ namespace Opm ws.thp = this->getTHPConstraint(summary_state); } else { // don't call for thp since this might trigger additional local solve - updateWellStateWithTarget(simulator, group_state, well_state, deferred_logger); + updateWellStateWithTarget(simulator, wgHelper, deferred_logger); } updatePrimaryVariables(simulator, well_state, deferred_logger); } @@ -382,14 +382,15 @@ namespace Opm WellInterface:: wellTesting(const Simulator& simulator, const double simulation_time, - /* const */ WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, WellTestState& well_test_state, GLiftEclWells& ecl_well_map, std::map& open_times, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); + const auto& group_state = wgHelper.groupState(); deferred_logger.info(" well " + this->name() + " is being tested"); WellStateType well_state_copy = well_state; @@ -430,7 +431,9 @@ namespace Opm // untill the number of closed completions do not increase anymore. while (testWell) { const std::size_t original_number_closed_completions = welltest_state_temp.num_closed_completions(); - bool converged = solveWellForTesting(simulator, well_state_copy, group_state, deferred_logger); + // Use RAII guard to temporarily redirect wgHelper to use well_state_copy + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + bool converged = solveWellForTesting(simulator, wgHelper, deferred_logger); if (!converged) { const auto msg = fmt::format("WTEST: Well {} is not solvable (physical)", this->name()); deferred_logger.debug(msg); @@ -438,7 +441,7 @@ namespace Opm } - updateWellOperability(simulator, well_state_copy, deferred_logger); + updateWellOperability(simulator, well_state_copy, wgHelper, deferred_logger); if ( !this->isOperableAndSolvable() ) { const auto msg = fmt::format("WTEST: Well {} is not operable (physical)", this->name()); deferred_logger.debug(msg); @@ -446,7 +449,7 @@ namespace Opm } std::vector potentials; try { - computeWellPotentials(simulator, well_state_copy, potentials, deferred_logger); + computeWellPotentials(simulator, well_state_copy, wgHelper, potentials, deferred_logger); } catch (const std::exception& e) { const std::string msg = fmt::format("well {}: computeWellPotentials() " "failed during testing for re-opening: ", @@ -502,11 +505,11 @@ namespace Opm WellInterface:: iterateWellEquations(const Simulator& simulator, const double dt, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); const auto& summary_state = simulator.vanguard().summaryState(); const auto inj_controls = this->well_ecl_.isInjector() ? this->well_ecl_.injectionControls(summary_state) : Well::InjectionControls(0); const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0); @@ -517,12 +520,12 @@ namespace Opm try { // TODO: the following two functions will be refactored to be one to reduce the code duplication if (!this->param_.local_well_solver_control_switching_){ - converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } else { if (this->param_.use_implicit_ipr_ && this->well_ecl_.isProducer() && (well_state.well(this->index_of_well_).status == WellStatus::OPEN)) { - converged = solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } else { - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } } @@ -567,25 +570,25 @@ namespace Opm const double dt, const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); const auto& summary_state = simulator.vanguard().summaryState(); bool converged = true; auto& ws = well_state.well(this->index_of_well_); // if well is stopped, check if we can reopen if (this->wellIsStopped()) { this->openWell(); - auto bhp_target = estimateOperableBhp(simulator, dt, well_state, summary_state, deferred_logger); + auto bhp_target = estimateOperableBhp(simulator, dt, wgHelper, summary_state, deferred_logger); if (!bhp_target.has_value()) { // no intersection with ipr const auto msg = fmt::format("estimateOperableBhp: Did not find operable BHP for well {}", this->name()); deferred_logger.debug(msg); // well can't operate using explicit fractions stop the well // solve with zero rates - converged = solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); + converged = solveWellWithZeroRate(simulator, dt, wgHelper, deferred_logger); this->stopWell(); this->operability_status_.can_obtain_bhp_with_thp_limit = false; this->operability_status_.obey_thp_limit_under_bhp_limit = false; @@ -595,11 +598,11 @@ namespace Opm ws.thp = this->getTHPConstraint(summary_state); const Scalar bhp = std::max(bhp_target.value(), static_cast(prod_controls.bhp_limit)); - solveWellWithBhp(simulator, dt, bhp, well_state, deferred_logger); + solveWellWithBhp(simulator, dt, bhp, wgHelper, deferred_logger); } } // solve well-equation - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); const bool isThp = ws.production_cmode == Well::ProducerCMode::THP; @@ -619,10 +622,10 @@ namespace Opm if (bhp_stable.has_value() && cur_bhp - bhp_stable.value() > cur_bhp*reltol){ const auto msg = fmt::format("Well {} converged to an unstable solution, re-solving", this->name()); deferred_logger.debug(msg); - solveWellWithBhp(simulator, dt, bhp_stable.value(), well_state, deferred_logger); + solveWellWithBhp(simulator, dt, bhp_stable.value(), wgHelper, deferred_logger); // re-solve with hopefully good initial guess ws.thp = this->getTHPConstraint(summary_state); - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } } } @@ -631,11 +634,11 @@ namespace Opm // Well did not converge, switch to explicit fractions this->operability_status_.use_vfpexplicit = true; this->openWell(); - auto bhp_target = estimateOperableBhp(simulator, dt, well_state, summary_state, deferred_logger); + auto bhp_target = estimateOperableBhp(simulator, dt, wgHelper, summary_state, deferred_logger); if (!bhp_target.has_value()) { // solve with zero rate // well can't operate using explicit fractions stop the well - converged = solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); + converged = solveWellWithZeroRate(simulator, dt, wgHelper, deferred_logger); this->stopWell(); this->operability_status_.can_obtain_bhp_with_thp_limit = false; this->operability_status_.obey_thp_limit_under_bhp_limit = false; @@ -644,15 +647,14 @@ namespace Opm // solve well with the estimated target bhp (or limit) const Scalar bhp = std::max(bhp_target.value(), static_cast(prod_controls.bhp_limit)); - solveWellWithBhp(simulator, dt, bhp, well_state, deferred_logger); + solveWellWithBhp(simulator, dt, bhp, wgHelper, deferred_logger); ws.thp = this->getTHPConstraint(summary_state); const auto msg = fmt::format("Well {} did not converge, re-solving with explicit fractions for VFP caculations.", this->name()); deferred_logger.debug(msg); converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, - well_state, - group_state, + wgHelper, deferred_logger); } } @@ -667,13 +669,14 @@ namespace Opm WellInterface:: estimateOperableBhp(const Simulator& simulator, const double dt, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) { + auto& well_state = const_cast(wgHelper).wellState(); if (!this->wellHasTHPConstraints(summary_state)) { const Scalar bhp_limit = WellBhpThpCalculator(*this).mostStrictBhpFromBhpLimits(summary_state); - const bool converged = solveWellWithBhp(simulator, dt, bhp_limit, well_state, deferred_logger); + const bool converged = solveWellWithBhp(simulator, dt, bhp_limit, wgHelper, deferred_logger); if (!converged || this->wellIsStopped()) { return std::nullopt; } @@ -685,7 +688,7 @@ namespace Opm // Get minimal bhp from vfp-curve Scalar bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity()); // Solve - const bool converged = solveWellWithBhp(simulator, dt, bhp_min, well_state, deferred_logger); + const bool converged = solveWellWithBhp(simulator, dt, bhp_min, wgHelper, deferred_logger); if (!converged || this->wellIsStopped()) { return std::nullopt; } @@ -701,12 +704,15 @@ namespace Opm solveWellWithBhp(const Simulator& simulator, const double dt, const Scalar bhp, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); // Solve a well using single bhp-constraint (but close if not operable under this) auto group_state = GroupState(); // empty group + auto group_guard = const_cast(wgHelper).pushGroupState(group_state); + auto inj_controls = Well::InjectionControls(0); auto prod_controls = Well::ProductionControls(0); auto& ws = well_state.well(this->index_of_well_); @@ -726,7 +732,7 @@ namespace Opm // update well-state ws.bhp = bhp; // solve - const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger, /*fixed_control*/true); + const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger, /*fixed_control*/true); ws.injection_cmode = cmode_inj; ws.production_cmode = cmode_prod; return converged; @@ -737,7 +743,7 @@ namespace Opm WellInterface:: solveWellWithZeroRate(const Simulator& simulator, const double dt, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); @@ -746,9 +752,11 @@ namespace Opm this->stopWell(); auto group_state = GroupState(); // empty group + auto group_guard = const_cast(wgHelper).pushGroupState(group_state); + auto inj_controls = Well::InjectionControls(0); auto prod_controls = Well::ProductionControls(0); - const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger, /*fixed_control*/true, /*fixed_status*/ true); + const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger, /*fixed_control*/true, /*fixed_status*/ true); this->wellStatus_ = well_status_orig; return converged; } @@ -757,11 +765,11 @@ namespace Opm bool WellInterface:: solveWellForTesting(const Simulator& simulator, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); const double dt = simulator.timeStepSize(); const auto& summary_state = simulator.vanguard().summaryState(); @@ -773,12 +781,12 @@ namespace Opm try { // TODO: the following two functions will be refactored to be one to reduce the code duplication if (!this->param_.local_well_solver_control_switching_){ - converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } else { if (this->param_.use_implicit_ipr_ && this->well_ecl_.isProducer() && (well_state.well(this->index_of_well_).status == WellStatus::OPEN)) { - converged = solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } else { - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); } } @@ -803,18 +811,18 @@ namespace Opm void WellInterface:: solveWellEquation(const Simulator& simulator, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return; + auto& well_state = const_cast(wgHelper).wellState(); // keep a copy of the original well state const WellStateType well_state0 = well_state; const double dt = simulator.timeStepSize(); - bool converged = iterateWellEquations(simulator, dt, well_state, group_state, deferred_logger); + bool converged = iterateWellEquations(simulator, dt, wgHelper, deferred_logger); // Newly opened wells with THP control sometimes struggles to // converge due to bad initial guess. Or due to the simple fact @@ -847,7 +855,7 @@ namespace Opm const std::string msg = std::string("The newly opened well ") + this->name() + std::string(" with THP control did not converge during inner iterations, we try again with bhp control"); deferred_logger.debug(msg); - converged = this->iterateWellEquations(simulator, dt, well_state, group_state, deferred_logger); + converged = this->iterateWellEquations(simulator, dt, wgHelper, deferred_logger); } } @@ -866,12 +874,13 @@ namespace Opm WellInterface:: assembleWellEq(const Simulator& simulator, const double dt, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - prepareWellBeforeAssembling(simulator, dt, well_state, group_state, deferred_logger); + auto& well_state = const_cast(wgHelper).wellState(); + auto& group_state = const_cast(wgHelper).groupState(); + prepareWellBeforeAssembling(simulator, dt, wgHelper, deferred_logger); assembleWellEqWithoutIteration(simulator, dt, well_state, group_state, deferred_logger); } @@ -902,15 +911,15 @@ namespace Opm WellInterface:: prepareWellBeforeAssembling(const Simulator& simulator, const double dt, - WellStateType& well_state, - const GroupState& group_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); const bool old_well_operable = this->operability_status_.isOperableAndSolvable(); if (this->param_.check_well_operability_iter_) - checkWellOperability(simulator, well_state, deferred_logger); + checkWellOperability(simulator, well_state, wgHelper, deferred_logger); // only use inner well iterations for the first newton iterations. const int iteration_idx = simulator.model().newtonMethod().numIterations(); @@ -933,7 +942,7 @@ namespace Opm } this->stopWell(); changed_to_stopped_this_step_ = true; - bool converged_zero_rate = this->solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); + bool converged_zero_rate = this->solveWellWithZeroRate(simulator, dt, wgHelper, deferred_logger); if (this->param_.shut_unsolvable_wells_ && !converged_zero_rate ) { this->operability_status_.solvable = false; } @@ -941,7 +950,7 @@ namespace Opm number_of_well_reopenings_++; return; } - bool converged = this->iterateWellEquations(simulator, dt, well_state, group_state, deferred_logger); + bool converged = this->iterateWellEquations(simulator, dt, wgHelper, deferred_logger); if (converged) { const bool zero_target = this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger); @@ -965,7 +974,7 @@ namespace Opm auto well_state_copy = well_state; std::vector potentials; try { - computeWellPotentials(simulator, well_state_copy, potentials, deferred_logger); + computeWellPotentials(simulator, well_state_copy, wgHelper, potentials, deferred_logger); } catch (const std::exception& e) { const std::string msg = fmt::format("well {}: computeWellPotentials() failed " "during attempt to recompute potentials for well: ", @@ -1038,6 +1047,7 @@ namespace Opm WellInterface:: checkWellOperability(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); @@ -1049,12 +1059,12 @@ namespace Opm return; } - updateWellOperability(simulator, well_state, deferred_logger); + updateWellOperability(simulator, well_state, wgHelper, deferred_logger); if (!this->operability_status_.isOperableAndSolvable()) { this->operability_status_.use_vfpexplicit = true; deferred_logger.debug("EXPLICIT_LOOKUP_VFP", "well not operable, trying with explicit vfp lookup: " + this->name()); - updateWellOperability(simulator, well_state, deferred_logger); + updateWellOperability(simulator, well_state, wgHelper, deferred_logger); } } @@ -1127,11 +1137,12 @@ namespace Opm WellInterface:: updateWellOperability(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); if (this->param_.local_well_solver_control_switching_) { - const bool success = updateWellOperabilityFromWellEq(simulator, well_state, deferred_logger); + const bool success = updateWellOperabilityFromWellEq(simulator, well_state, wgHelper, deferred_logger); if (!success) { this->operability_status_.solvable = false; deferred_logger.debug("Operability check using well equations did not converge for well " @@ -1155,7 +1166,7 @@ namespace Opm } // we do some extra checking for wells under THP control. if (check_thp) { - checkOperabilityUnderTHPLimit(simulator, well_state, deferred_logger); + checkOperabilityUnderTHPLimit(simulator, well_state, wgHelper, deferred_logger); } } @@ -1164,6 +1175,7 @@ namespace Opm WellInterface:: updateWellOperabilityFromWellEq(const Simulator& simulator, const WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); @@ -1171,10 +1183,10 @@ namespace Opm assert(this->param_.local_well_solver_control_switching_); this->operability_status_.resetOperability(); WellStateType well_state_copy = well_state; - const auto& group_state = simulator.problem().wellModel().groupState(); const double dt = simulator.timeStepSize(); // equations should be converged at this stage, so only one it is needed - bool converged = iterateWellEquations(simulator, dt, well_state_copy, group_state, deferred_logger); + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + bool converged = iterateWellEquations(simulator, dt, wgHelper, deferred_logger); return converged; } @@ -1190,11 +1202,12 @@ namespace Opm void WellInterface:: updateWellStateWithTarget(const Simulator& simulator, - const GroupState& group_state, - WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const { OPM_TIMEFUNCTION(); + auto& well_state = const_cast(wgHelper).wellState(); + const auto& group_state = wgHelper.groupState(); // only bhp and wellRates are used to initilize the primaryvariables for standard wells const auto& well = this->well_ecl_; const int well_index = this->index_of_well_; @@ -1515,7 +1528,7 @@ namespace Opm } case Well::ProducerCMode::THP: { - const bool update_success = updateWellStateWithTHPTargetProd(simulator, well_state, deferred_logger); + const bool update_success = updateWellStateWithTHPTargetProd(simulator, well_state, wgHelper, deferred_logger); if (!update_success) { // the following is the original way of initializing well state with THP constraint @@ -2037,18 +2050,19 @@ namespace Opm WellInterface:: updateWellStateWithTHPTargetProd(const Simulator& simulator, WellStateType& well_state, + const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) const { OPM_TIMEFUNCTION(); const auto& summary_state = simulator.vanguard().summaryState(); auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq( - simulator, summary_state, this->getALQ(well_state), deferred_logger, /*iterate_if_no_solution */ false); + simulator, wgHelper, summary_state, this->getALQ(well_state), deferred_logger, /*iterate_if_no_solution */ false); if (bhp_at_thp_limit) { std::vector rates(this->number_of_phases_, 0.0); if (thp_update_iterations) { computeWellRatesWithBhpIterations(simulator, *bhp_at_thp_limit, - rates, deferred_logger); + wgHelper, rates, deferred_logger); } else { computeWellRatesWithBhp(simulator, *bhp_at_thp_limit, rates, deferred_logger); From 2d72d25a4de54bea1af603fe682932d3bb02a993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 14 Oct 2025 08:44:47 +0200 Subject: [PATCH 02/26] Correct macro name --- opm/simulators/wells/WellGroupHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index 24bffa464d..3078465511 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -1986,7 +1986,7 @@ updateGroupTargetReductionRecursive_( template class WellGroupHelper; -#ifdef HAVE_FLOAT +#ifdef FLOW_INSTANTIATE_FLOAT template class WellGroupHelper; #endif } // namespace Opm From 0a8e67033ea10d189118c784c4e3d96cc0cfcdbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 14 Oct 2025 10:13:10 +0200 Subject: [PATCH 03/26] Remove unused parameter warnings --- opm/simulators/wells/WellGroupConstraints.cpp | 12 ++++-------- opm/simulators/wells/WellGroupConstraints.hpp | 7 ++----- opm/simulators/wells/WellInterfaceFluidSystem.cpp | 7 +++---- opm/simulators/wells/WellInterfaceFluidSystem.hpp | 3 +-- opm/simulators/wells/WellInterface_impl.hpp | 4 ++-- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/opm/simulators/wells/WellGroupConstraints.cpp b/opm/simulators/wells/WellGroupConstraints.cpp index f360eb4c2a..9bef5e01ce 100644 --- a/opm/simulators/wells/WellGroupConstraints.cpp +++ b/opm/simulators/wells/WellGroupConstraints.cpp @@ -40,11 +40,9 @@ WellGroupConstraints:: checkGroupConstraintsInj(const Group& group, const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, - const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const + const bool check_guide_rate) const { auto& well_state = const_cast(wgHelper).wellState(); @@ -122,8 +120,7 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const + const bool check_guide_rate) const { auto& well_state = const_cast(wgHelper).wellState(); const auto& well = well_.wellEcl(); @@ -145,10 +142,9 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, well_state[well.name()].efficiency_scaling_factor; const std::pair group_constraint = this->checkGroupConstraintsInj(group, wgHelper, efficiencyFactor, - schedule, summaryState, + summaryState, rateConverter, - check_guide_rate, - deferred_logger); + check_guide_rate); // If a group constraint was broken, we set the current well control to // be GRUP. if (group_constraint.first) { diff --git a/opm/simulators/wells/WellGroupConstraints.hpp b/opm/simulators/wells/WellGroupConstraints.hpp index c501739099..f15d16a8b8 100644 --- a/opm/simulators/wells/WellGroupConstraints.hpp +++ b/opm/simulators/wells/WellGroupConstraints.hpp @@ -61,19 +61,16 @@ public: const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const; + const bool check_guide_rate) const; private: std::pair checkGroupConstraintsInj(const Group& group, const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, - const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const; + const bool check_guide_rate) const; std::pair checkGroupConstraintsProd(const Group& group, diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index b3fddfee13..00e93a10e2 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -182,8 +182,7 @@ WellInterfaceFluidSystem:: checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const + const bool check_guide_rate) const { const auto& group_state = wgHelper.groupState(); @@ -205,7 +204,7 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, return WellGroupConstraints(*this).checkGroupConstraints(wgHelper, schedule, summaryState, - rCoeff, check_guide_rate, deferred_logger); + rCoeff, check_guide_rate); } template @@ -223,7 +222,7 @@ checkConstraints(const WellGroupHelperType& wgHelper, return true; } else { return checkGroupConstraints(wgHelper, schedule, - summaryState, true, deferred_logger); + summaryState, true); } } diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.hpp b/opm/simulators/wells/WellInterfaceFluidSystem.hpp index 6f35bf1676..08605045a0 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.hpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.hpp @@ -93,8 +93,7 @@ protected: bool checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, - const bool check_guide_rate, - DeferredLogger& deferred_logger) const; + const bool check_guide_rate) const; bool checkConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 1c3de2eaf8..69cf0478f6 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -233,7 +233,7 @@ namespace Opm if (iog == IndividualOrGroup::Individual) { changed = this->checkIndividualConstraints(ws, summaryState, deferred_logger); } else if (iog == IndividualOrGroup::Group) { - changed = this->checkGroupConstraints(wgHelper, schedule, summaryState, true, deferred_logger); + changed = this->checkGroupConstraints(wgHelper, schedule, summaryState, true); } else { assert(iog == IndividualOrGroup::Both); changed = this->checkConstraints(wgHelper, schedule, summaryState, deferred_logger); @@ -317,7 +317,7 @@ namespace Opm changed = this->checkIndividualConstraints(ws, summary_state, deferred_logger, inj_controls, prod_controls); } if (hasGroupControl && this->param_.check_group_constraints_inner_well_iterations_) { - changed = changed || this->checkGroupConstraints(wgHelper, schedule, summary_state, false, deferred_logger); + changed = changed || this->checkGroupConstraints(wgHelper, schedule, summary_state, false); } if (changed) { From 113d64def82102dc03bb6d86679a48ac7c11ab02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Thu, 16 Oct 2025 13:13:10 +0200 Subject: [PATCH 04/26] Fix bug introduced in refactoring Refactoring incorrectly added gas phase to liquid rate calculations, which caused the regression tests to fail. --- opm/simulators/wells/BlackoilWellModelConstraints.cpp | 3 --- opm/simulators/wells/WellGroupHelper.hpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index 50e20a5d52..3892a46ef2 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -255,9 +255,6 @@ checkGroupProductionConstraints(const Group& group, DeferredLogger& deferred_log current_rate += wgHelper().sumWellSurfaceRates(group, pu.canonicalToActivePhaseIdx(waterPhaseIdx), /*is_injector=*/false); - current_rate += wgHelper().sumWellSurfaceRates(group, - pu.canonicalToActivePhaseIdx(gasPhaseIdx), - /*is_injector=*/false); // sum over all nodes current_rate = wellModel_.comm().sum(current_rate); diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index 6f3c47325e..e9f3618547 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -232,7 +232,7 @@ private: const Schedule& schedule_; const SummaryState& summary_state_; - WellState* well_state_; + WellState* well_state_{nullptr}; GroupState* group_state_{nullptr}; const GuideRate& guide_rate_; int report_step_{0}; From 88f71309656c16b92e6e6e6aa550d3394cbdbd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 17 Oct 2025 09:59:08 +0200 Subject: [PATCH 05/26] Remove branching that discoordinates split wells See https://github.com/OPM/opm-simulators/pull/6544 for more information --- opm/simulators/wells/WellGroupHelper.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index 3078465511..d3139e36a7 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -1427,11 +1427,6 @@ updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool if (!well_index.has_value()) continue; - if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once - { - continue; - } - auto& ws = this->wellState().well(well_index.value()); if (ws.status == Well::Status::SHUT) continue; From 42b55c59b280eefb6fb40287f42b8d9a360f2e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 21 Oct 2025 06:45:39 +0200 Subject: [PATCH 06/26] Add const qualifier to 7 read-only methods in WellGroupHelper Improve const-correctness by marking methods that don't modify member state as const: - 5 public methods (accumulateGroupEfficiencyFactor, getWellGroupTarget*, etc.) - 2 private helpers (controlGroup_, phaseToActivePhaseIdx_) --- opm/simulators/wells/WellGroupHelper.cpp | 12 ++++++------ opm/simulators/wells/WellGroupHelper.hpp | 17 +++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index d3139e36a7..c88d2b60bc 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -53,7 +53,7 @@ WellGroupHelper::WellGroupHelper( template void WellGroupHelper:: -accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) +accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) const { factor *= group.getGroupEfficiencyFactor(); if (group.parent() != "FIELD" && !group.parent().empty()) @@ -658,7 +658,7 @@ getWellGroupTargetInjector(const std::string& name, const Scalar* rates, Phase injection_phase, const Scalar efficiency_factor, - const std::vector& resv_coeff) + const std::vector& resv_coeff) const { // This function computes a wells group target. // 'parent' will be the name of 'group'. But if we recurse, 'name' and @@ -794,7 +794,7 @@ getWellGroupTargetProducer(const std::string& name, const Group& group, const Scalar* rates, const Scalar efficiency_factor, - const std::vector& resv_coeff) + const std::vector& resv_coeff) const { // This function computes a wells group target. // 'parent' will be the name of 'group'. But if we recurse, 'name' and @@ -1039,7 +1039,7 @@ setCmodeGroup(const Group& group) template Scalar WellGroupHelper:: -sumSolventRates(const Group& group, const bool is_injector) +sumSolventRates(const Group& group, const bool is_injector) const { Scalar rate = 0.0; for (const std::string& group_name : group.groups()) { @@ -1569,7 +1569,7 @@ worstOffendingWell(const Group& group, template std::string WellGroupHelper:: -controlGroup_(const Group& group) +controlGroup_(const Group& group) const { const Group::ProductionCMode& currentGroupControl = this->groupState().production_control(group.name()); @@ -1634,7 +1634,7 @@ isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const template int WellGroupHelper:: -phaseToActivePhaseIdx_(const Phase phase) +phaseToActivePhaseIdx_(const Phase phase) const { const auto& pu = this->phase_usage_info_; int phase_pos = -1; diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index e9f3618547..d8019b7bfb 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -104,7 +104,7 @@ public: const PhaseUsageInfo& phase_usage_info ); - void accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor); + void accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) const; std::pair checkGroupConstraintsInj( const std::string& name, const std::string& parent, @@ -140,7 +140,7 @@ public: const Phase injection_phase, const Scalar efficiency_factor, const std::vector& resv_coeff - ); + ) const; Scalar getWellGroupTargetProducer( const std::string& name, const std::string& parent, @@ -148,7 +148,7 @@ public: const Scalar* rates, const Scalar efficiency_factor, const std::vector& resv_coeff - ); + ) const; GuideRate::RateVector getWellRateVector(const std::string& name) const; std::vector groupChainTopBot(const std::string& bottom, const std::string& top) const; int groupControlledWells( @@ -173,8 +173,8 @@ public: const Group& group, const FieldPropsManager& fp, std::map>& regional_average_pressure_calculator - ); - Scalar sumSolventRates(const Group& group, const bool is_injector); + ) const; + Scalar sumSolventRates(const Group& group, const bool is_injector) const; Scalar sumWellResRates(const Group& group, const int phase_pos, const bool injector) const; Scalar sumWellSurfaceRates(const Group& group, const int phase_pos, const bool injector) const; Scalar sumWellPhaseRates( @@ -208,10 +208,10 @@ public: const Parallel::Communication& comm ) const; private: - std::string controlGroup_(const Group& group); + std::string controlGroup_(const Group& group) const; GuideRate::RateVector getGuideRateVector_(const std::vector& rates) const; bool isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const; - int phaseToActivePhaseIdx_(const Phase phase); + int phaseToActivePhaseIdx_(const Phase phase) const; Scalar satelliteInjectionRate_( const ScheduleState& sched, const Group& group, @@ -257,7 +257,8 @@ void WellGroupHelper:: setRegionAveragePressureCalculator( const Group& group, const FieldPropsManager& fp, - std::map>& regional_average_pressure_calculator) + std::map>& regional_average_pressure_calculator +) const { for (const std::string& groupName : group.groups()) { this->setRegionAveragePressureCalculator(this->schedule_.getGroup(groupName, this->report_step_), fp, regional_average_pressure_calculator); From a19c9a729781abf72e8c935c72befc12c60b74e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 21 Oct 2025 07:03:29 +0200 Subject: [PATCH 07/26] Improved initialization of WGHelper class To make the update of the WellState and GroupState pointer in WellGroupHelper less fragile: - Moved initial initialization of the WellState and GroupState pointers to the constructor of BlackoilWellModelGeneric. - Moved updateState() to resetWGState() --- opm/simulators/wells/BlackoilWellModelGeneric.cpp | 2 +- opm/simulators/wells/BlackoilWellModelGeneric.hpp | 2 ++ opm/simulators/wells/BlackoilWellModel_impl.hpp | 5 ++--- opm/simulators/wells/WellGroupHelper.cpp | 9 ++++++--- opm/simulators/wells/WellGroupHelper.hpp | 9 ++++++--- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index dbf2794da5..0a112c44c6 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -110,7 +110,7 @@ BlackoilWellModelGeneric(Schedule& schedule, , active_wgstate_(pu) , last_valid_wgstate_(pu) , nupcol_wgstate_(pu) - , wg_helper_(schedule, summaryState, guideRate_, pu) + , wg_helper_(this->wellState(), this->groupState(), this->schedule(), summaryState, guideRate_, pu) { const auto numProcs = comm_.size(); diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index 189fc82246..a94bbea0bc 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -359,6 +359,8 @@ protected: { this->active_wgstate_ = this->last_valid_wgstate_; this->node_pressures_ = this->last_valid_node_pressures_; + // Update helper pointers to reference the restored active state + this->wg_helper_.updateState(this->wellState(), this->groupState()); } /* diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 5fdd6801e3..8c79d8d7e1 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -195,7 +195,7 @@ namespace Opm { { DeferredLogger local_deferredLogger{}; - this->wgHelper().updateState(this->wellState(), this->groupState(), timeStepIdx); + this->wgHelper().setReportStep(timeStepIdx); this->wgHelper().setLogger(&local_deferredLogger); this->report_step_starts_ = true; this->report_step_start_events_ = this->schedule()[timeStepIdx].wellgroup_events(); @@ -333,6 +333,7 @@ namespace Opm { this->updateAverageFormationFactor(); DeferredLogger local_deferredLogger; + this->wgHelper().setLogger(&local_deferredLogger); this->switched_prod_groups_.clear(); this->switched_inj_groups_.clear(); @@ -363,8 +364,6 @@ namespace Opm { this->resetWGState(); const int reportStepIdx = simulator_.episodeIndex(); - this->wgHelper().updateState(this->wellState(), this->groupState(), reportStepIdx); - this->wgHelper().setLogger(&local_deferredLogger); this->wellState().updateWellsDefaultALQ(this->schedule(), reportStepIdx, this->summaryState()); this->wellState().gliftTimeStepInit(); diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index c88d2b60bc..b579b3712a 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -34,12 +34,16 @@ namespace Opm { template WellGroupHelper::WellGroupHelper( + WellState& well_state, + GroupState& group_state, const Schedule& schedule, const SummaryState& summary_state, const GuideRate& guide_rate, const PhaseUsageInfo& phase_usage_info -) : schedule_{schedule} +) : well_state_{&well_state} + , group_state_{&group_state} + , schedule_{schedule} , summary_state_{summary_state} , guide_rate_{guide_rate} , phase_usage_info_{phase_usage_info} @@ -1306,11 +1310,10 @@ updateReservoirRatesInjectionGroups(const Group& group) template void WellGroupHelper:: -updateState(WellState& well_state, GroupState& group_state, int report_step) +updateState(WellState& well_state, GroupState& group_state) { this->well_state_ = &well_state; this->group_state_ = &group_state; - this->report_step_ = report_step; } template diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index d8019b7bfb..e528294dd1 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -98,6 +98,8 @@ public: }; WellGroupHelper( + WellState& well_state, + GroupState& group_state, const Schedule& schedule, const SummaryState& summary_state, const GuideRate& guide_rate, @@ -174,6 +176,7 @@ public: const FieldPropsManager& fp, std::map>& regional_average_pressure_calculator ) const; + void setReportStep(int report_step) { report_step_ = report_step; } Scalar sumSolventRates(const Group& group, const bool is_injector) const; Scalar sumWellResRates(const Group& group, const int phase_pos, const bool injector) const; Scalar sumWellSurfaceRates(const Group& group, const int phase_pos, const bool injector) const; @@ -195,7 +198,7 @@ public: void updateReservoirRatesInjectionGroups(const Group& group); void updateVREPForGroups(const Group& group); void updateState( - WellState& well_state, GroupState& group_state, int report_step + WellState& well_state, GroupState& group_state ); void updateSurfaceRatesInjectionGroups(const Group& group); void updateWellRates(const Group& group, const WellState& well_state_nupcol); @@ -230,10 +233,10 @@ private: void updateGroupTargetReductionRecursive_( const Group& group, const bool is_injector, std::vector& group_target_reduction); - const Schedule& schedule_; - const SummaryState& summary_state_; WellState* well_state_{nullptr}; GroupState* group_state_{nullptr}; + const Schedule& schedule_; + const SummaryState& summary_state_; const GuideRate& guide_rate_; int report_step_{0}; DeferredLogger* deferred_logger_{nullptr}; From bef77ec3f06d0c535fcb53d7d9dae147350e8ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 21 Oct 2025 07:16:39 +0200 Subject: [PATCH 08/26] Clarify the guard and pushWellState pattern Clarify how the guard and pushWellState() pattern works --- opm/simulators/wells/BlackoilWellModelGeneric.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 0a112c44c6..3f5cf5b190 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -1302,6 +1302,8 @@ updateAndCommunicateGroupData(const int reportStepIdx, const int np = this->wellState().numPhases(); Scalar gr_rate_nupcol = 0.0; { + // Temporarily use the nupcol well state for all helper functions + // At the end of this scope, the well state will be restored to its original value auto guard = wg_helper.pushWellState(this->nupcolWellState()); for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { gr_rate_nupcol += wg_helper.sumWellPhaseRates( @@ -1350,6 +1352,8 @@ updateAndCommunicateGroupData(const int reportStepIdx, wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/false); wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/true); { + // Temporarily use the nupcol well state for all helper functions + // At the end of this scope, the well state will be restored to its original value auto guard = wg_helper.pushWellState(this->nupcolWellState()); wg_helper.updateREINForGroups(fieldGroup, /*sum_rank=*/comm_.rank() == 0); wg_helper.updateVREPForGroups(fieldGroup); From 95e66f38f7178d05fd73d736158b8292607bff77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 21 Oct 2025 07:30:25 +0200 Subject: [PATCH 09/26] Clarify why non-const accessor is needed Clarify why we need a non-const nupcolWellState() accessor. --- opm/simulators/wells/BlackoilWellModelGeneric.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index a94bbea0bc..2b51c252c3 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -165,6 +165,10 @@ public: /* Will return the currently active nupcolWellState; must update the internal nupcol wellstate with updateNupcolWGState() first. + + Both const and non-const accessors are provided. The non-const + accessor is required for the WellStateGuard pattern and pushWellState() + in WellGroupHelper, which temporarily switches WellGroupHelper to use this state. */ const WellState& nupcolWellState() const { From 47a96a30fe5f4cb09e4bb788b889a5e7ba302a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Wed, 22 Oct 2025 13:59:06 +0200 Subject: [PATCH 10/26] Remove old WellGroupHelpers type Final cleanup: Remove old WellGroupHelpers type, and fix one missing refactorization. --- CMakeLists_files.cmake | 2 - opm/simulators/wells/BlackoilWellModel.hpp | 3 - .../wells/BlackoilWellModelGeneric.cpp | 1 - opm/simulators/wells/FractionCalculator.cpp | 1 - .../wells/GroupEconomicLimitsChecker.cpp | 12 +- opm/simulators/wells/WellGroupHelpers.hpp | 288 ------------------ 6 files changed, 4 insertions(+), 303 deletions(-) delete mode 100644 opm/simulators/wells/WellGroupHelpers.hpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 4941498539..1657666eb0 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -244,7 +244,6 @@ list (APPEND MAIN_SOURCE_FILES opm/simulators/wells/WellGroupConstraints.cpp opm/simulators/wells/WellGroupControls.cpp opm/simulators/wells/WellGroupHelper.cpp - opm/simulators/wells/WellGroupHelpers.cpp opm/simulators/wells/WellHelpers.cpp opm/simulators/wells/WellInterfaceFluidSystem.cpp opm/simulators/wells/WellInterfaceGeneric.cpp @@ -1160,7 +1159,6 @@ list (APPEND PUBLIC_HEADER_FILES opm/simulators/wells/WellGroupConstraints.hpp opm/simulators/wells/WellGroupControls.hpp opm/simulators/wells/WellGroupHelper.hpp - opm/simulators/wells/WellGroupHelpers.hpp opm/simulators/wells/WellHelpers.hpp opm/simulators/wells/WellInterfaceFluidSystem.hpp opm/simulators/wells/WellInterfaceGeneric.hpp diff --git a/opm/simulators/wells/BlackoilWellModel.hpp b/opm/simulators/wells/BlackoilWellModel.hpp index c69a2df587..946eae9170 100644 --- a/opm/simulators/wells/BlackoilWellModel.hpp +++ b/opm/simulators/wells/BlackoilWellModel.hpp @@ -73,7 +73,6 @@ #include #include #include -#include #include #include #include @@ -144,8 +143,6 @@ template class WellContributions; using AverageRegionalPressureType = RegionAverageCalculator:: AverageRegionalPressure >; - using WellGroupHelpersType = WellGroupHelpers; - explicit BlackoilWellModel(Simulator& simulator); void init(); diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 3f5cf5b190..1160096b7a 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -65,7 +65,6 @@ #include #include #include -#include #include #include diff --git a/opm/simulators/wells/FractionCalculator.cpp b/opm/simulators/wells/FractionCalculator.cpp index c7050b96eb..f589ae97fe 100644 --- a/opm/simulators/wells/FractionCalculator.cpp +++ b/opm/simulators/wells/FractionCalculator.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include diff --git a/opm/simulators/wells/GroupEconomicLimitsChecker.cpp b/opm/simulators/wells/GroupEconomicLimitsChecker.cpp index 64d5550b37..1c56bd008f 100644 --- a/opm/simulators/wells/GroupEconomicLimitsChecker.cpp +++ b/opm/simulators/wells/GroupEconomicLimitsChecker.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include @@ -79,13 +79,9 @@ GroupEconomicLimitsChecker(const BlackoilWellModelGeneric& auto phase_idx = this->phase_idx_map_[i]; this->phase_idx_reverse_map_[phase_idx] = static_cast(i); auto phase_pos = this->well_model_.phaseUsage().canonicalToActivePhaseIdx(phase_idx); - Scalar production_rate = WellGroupHelpers::sumWellSurfaceRates(this->group_, - this->schedule_, - this->well_state_, - this->report_step_idx_, - phase_pos, - /*isInjector*/false, - well_model_.summaryState()); + Scalar production_rate = this->well_model_.wgHelper().sumWellSurfaceRates( + this->group_, phase_pos, /*isInjector*/false + ); this->production_rates_[i] = this->well_model_.comm().sum(production_rate); } } diff --git a/opm/simulators/wells/WellGroupHelpers.hpp b/opm/simulators/wells/WellGroupHelpers.hpp deleted file mode 100644 index 399e0a93da..0000000000 --- a/opm/simulators/wells/WellGroupHelpers.hpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - Copyright 2019 Norce. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - - -#ifndef OPM_WELLGROUPHELPERS_HEADER_INCLUDED -#define OPM_WELLGROUPHELPERS_HEADER_INCLUDED - -#include "opm/material/fluidsystems/PhaseUsageInfo.hpp" - -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace Opm { - -class DeferredLogger; -class Group; -template class GroupState; -namespace Network { class ExtNetwork; } -class Schedule; -template class VFPProdProperties; -template class WellState; -class FieldPropsManager; - -namespace Network { class ExtNetwork; } - -template -class WellGroupHelpers -{ -public: - - using WellStateType = WellState; - - static Scalar sumWellPhaseRates(bool res_rates, - const Opm::Group& group, - const Opm::Schedule& schedule, - const WellStateType& wellState, - const SummaryState& summaryState, - const int reportStepIdx, - const int phasePos, - const bool injector, - const bool network = false); - - static Scalar satelliteInjectionRate(const ScheduleState& sched, - const Group& group, - const PhaseUsageInfo& pu, - const int phase_pos, - bool res_rates); - - static Scalar satelliteProductionRate(const SummaryState& summaryState, - const ScheduleState& sched, - const Group& group, - const GSatProd::GSatProdGroupProp::Rate rateComp, - bool res_rates); - - static std::optional - selectRateComponent(const PhaseUsageInfo& pu, const int phasePos); - - static void setCmodeGroup(const Group& group, - const Schedule& schedule, - const SummaryState& summaryState, - const int reportStepIdx, - GroupState& group_state); - - static void accumulateGroupEfficiencyFactor(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - Scalar& factor); - - static Scalar sumWellSurfaceRates(const Group& group, - const Schedule& schedule, - const WellStateType& wellState, - const int reportStepIdx, - const int phasePos, - const bool injector, - const SummaryState& summaryState); - - /// Returns the name of the worst offending well and its fraction (i.e. violated_phase / preferred_phase) - static std::pair, Scalar> - worstOffendingWell(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const Group::ProductionCMode& offendedControl, - const Parallel::Communication& comm, - const WellStateType& wellState, - DeferredLogger& deferred_logger); - - static Scalar sumWellResRates(const Group& group, - const Schedule& schedule, - const WellStateType& wellState, - const int reportStepIdx, - const int phasePos, - const bool injector, - const SummaryState& summaryState); - - static Scalar sumSolventRates(const Group& group, - const Schedule& schedule, - const WellStateType& wellState, - const int reportStepIdx, - const bool injector); - - static void updateGroupTargetReduction(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const bool isInjector, - const GuideRate& guide_rate, - const WellStateType& wellState, - const SummaryState& summaryState, - GroupState& group_state, - std::vector& groupTargetReduction); - - static void updateVREPForGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellStateType& wellState, - GroupState& group_state, - const SummaryState& summaryState); - - template - static void updateGpMaintTargetForGroups(const Group& group, - const Schedule& schedule, - const RegionalValues& regional_values, - const int reportStepIdx, - const double dt, - const WellStateType& well_state, - GroupState& group_state); - - static void updateReservoirRatesInjectionGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellStateType& wellState, - GroupState& group_state, - const SummaryState& summaryState); - - static void updateSurfaceRatesInjectionGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellStateType& wellState, - GroupState& group_state, - const SummaryState& summaryState); - - static void updateWellRates(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellStateType& wellStateNupcol, - WellStateType& wellState); - - static void updateGroupProductionRates(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellStateType& wellState, - GroupState& group_state, - const SummaryState& summaryState); - - static void updateNetworkLeafNodeProductionRates(const Schedule& schedule, - const int reportStepIdx, - const WellStateType& wellState, - GroupState& group_state, - const SummaryState& summaryState); - - - static void updateWellRatesFromGroupTargetScale(const Scalar scale, - const Group& group, - const Schedule& schedule, - const int reportStepIdx, - bool isInjector, - const GroupState& group_state, - WellStateType& wellState); - - static void updateREINForGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const SummaryState& st, - const WellStateType& wellState, - GroupState& group_state, - bool sum_rank); - - - static std::map - computeNetworkPressures(const Network::ExtNetwork& network, - const WellStateType& well_state, - const GroupState& group_state, - const VFPProdProperties& vfp_prod_props, - const Schedule& schedule, - const Parallel::Communication& comm, - const int report_time_step); - - static GuideRate::RateVector - getWellRateVector(const WellStateType& well_state, - const std::string& name); - - static GuideRate::RateVector - getProductionGroupRateVector(const GroupState& group_state, - const PhaseUsageInfo& pu, - const std::string& group_name); - - static Scalar getGuideRate(const std::string& name, - const Schedule& schedule, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const GuideRateModel::Target target); - - static Scalar getGuideRateInj(const std::string& name, - const Schedule& schedule, - const WellStateType& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const GuideRateModel::Target target, - const Phase& injectionPhase); - - /// update the number of wells that are actively under group control for a given group with name given by group_name - /// its main usage is to detect cases where there is no wells under group control - static int updateGroupControlledWells(const Schedule& schedule, - const WellStateType& well_state, - GroupState& group_state, - const SummaryState& summary_state, - const GuideRate* guideRate, - const int report_step, - const std::string& group_name, - const bool is_production_group, - const Phase injection_phase); - - /// returns the number of wells that are actively under group control for a given group with name given by group_name - static int groupControlledWells(const Schedule& schedule, - const WellStateType& well_state, - const GroupState& group_state, - const int report_step, - const std::string& group_name, - const std::string& always_included_child, - const bool is_production_group, - const Phase injection_phase); - - static std::vector - groupChainTopBot(const std::string& bottom, - const std::string& top, - const Schedule& schedule, - const int report_step); - - - // check if well/group bottom is a sub well/group of the group top - static bool - isInGroupChainTopBot(const std::string& bottom, - const std::string& top, - const Schedule& schedule, - const int report_step); - - static std::string - control_group(const Group& group, - const GroupState& group_state, - const int reportStepIdx, - const Schedule& schedule); - - template - static void setRegionAveragePressureCalculator(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const FieldPropsManager& fp, - std::map>& regionalAveragePressureCalculator); -}; - -} // namespace Opm - -#endif From fc2392b2cba3214795a8702a1b1840d9e4283a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Thu, 23 Oct 2025 08:14:41 +0200 Subject: [PATCH 11/26] Cleanup unecessary casts Remove unecessary GroupState const_casts --- opm/simulators/wells/BlackoilWellModelConstraints.cpp | 1 - opm/simulators/wells/BlackoilWellModelGeneric.hpp | 2 +- opm/simulators/wells/WellGroupConstraints.cpp | 4 ++-- opm/simulators/wells/WellInterface_impl.hpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index 3892a46ef2..22923c3437 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -265,7 +265,6 @@ checkGroupProductionConstraints(const Group& group, DeferredLogger& deferred_log wgHelper().sumWellSurfaceRates(group, pu.canonicalToActivePhaseIdx(waterPhaseIdx), /*is_injector=*/false); - current_water_rate = wellModel_.comm().sum(current_water_rate); current_water_rate = wellModel_.comm().sum(current_water_rate); if (std::abs(current_water_rate) < 1e-12) { skip = true; diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index 2b51c252c3..9b546db66c 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -588,7 +588,7 @@ protected: WGState active_wgstate_; WGState last_valid_wgstate_; WGState nupcol_wgstate_; - WellGroupHelperType wg_helper_{}; + WellGroupHelperType wg_helper_; WellGroupEvents report_step_start_events_; //!< Well group events at start of report step bool wellStructureChangedDynamically_{false}; diff --git a/opm/simulators/wells/WellGroupConstraints.cpp b/opm/simulators/wells/WellGroupConstraints.cpp index 9bef5e01ce..c9c606760c 100644 --- a/opm/simulators/wells/WellGroupConstraints.cpp +++ b/opm/simulators/wells/WellGroupConstraints.cpp @@ -44,7 +44,7 @@ checkGroupConstraintsInj(const Group& group, const RateConvFunc& rateConverter, const bool check_guide_rate) const { - auto& well_state = const_cast(wgHelper).wellState(); + const auto& well_state = wgHelper.wellState(); // Translate injector type from control to Phase. const auto& well_controls = well_.wellEcl().injectionControls(summaryState); @@ -97,7 +97,7 @@ checkGroupConstraintsProd(const Group& group, const RateConvFunc& rateConverter, const bool check_guide_rate) const { - auto& well_state = const_cast(wgHelper).wellState(); + const auto& well_state = wgHelper.wellState(); // Make conversion factors for RESV <-> surface rates. std::vector resv_coeff(well_.phaseUsage().numActivePhases(), 1.0); diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 69cf0478f6..9958e34ad3 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -879,7 +879,7 @@ namespace Opm { OPM_TIMEFUNCTION(); auto& well_state = const_cast(wgHelper).wellState(); - auto& group_state = const_cast(wgHelper).groupState(); + const auto& group_state = wgHelper.groupState(); prepareWellBeforeAssembling(simulator, dt, wgHelper, deferred_logger); assembleWellEqWithoutIteration(simulator, dt, well_state, group_state, deferred_logger); } From 4283f623054eb7fdb09984a4f3e47f5d1eea4b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Thu, 23 Oct 2025 08:22:32 +0200 Subject: [PATCH 12/26] Make setCmodeGroup() const by passing non-const GroupState --- .../wells/BlackoilWellModel_impl.hpp | 2 +- opm/simulators/wells/WellGroupHelper.cpp | 24 +++++++++---------- opm/simulators/wells/WellGroupHelper.hpp | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 8c79d8d7e1..10f4355e2d 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -301,7 +301,7 @@ namespace Opm { const auto& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx); - this->wgHelper().setCmodeGroup(fieldGroup); + this->wgHelper().setCmodeGroup(this->groupState(), fieldGroup); // Define per region average pressure calculators for use by // pressure maintenance groups (GPMAINT keyword). diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index b579b3712a..3f99344661 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -982,22 +982,22 @@ groupControlledWells(const std::string& group_name, template void WellGroupHelper:: -setCmodeGroup(const Group& group) +setCmodeGroup(GroupState& group_state, const Group& group) const { for (const std::string& group_name : group.groups()) { - this->setCmodeGroup(this->schedule_.getGroup(group_name, this->report_step_)); + this->setCmodeGroup(group_state, this->schedule_.getGroup(group_name, this->report_step_)); } // use NONE as default control const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; for (Phase phase : all) { - if (!this->groupState().has_injection_control(group.name(), phase)) { - this->groupState().injection_control(group.name(), phase, Group::InjectionCMode::NONE); + if (!group_state.has_injection_control(group.name(), phase)) { + group_state.injection_control(group.name(), phase, Group::InjectionCMode::NONE); } } - if (!this->groupState().has_production_control(group.name())) { - this->groupState().production_control(group.name(), Group::ProductionCMode::NONE); + if (!group_state.has_production_control(group.name())) { + group_state.production_control(group.name(), Group::ProductionCMode::NONE); } const auto& events = this->schedule_[this->report_step_].wellgroup_events(); @@ -1009,29 +1009,29 @@ setCmodeGroup(const Group& group) continue; const auto& controls = group.injectionControls(phase, this->summary_state_); - this->groupState().injection_control(group.name(), phase, controls.cmode); + group_state.injection_control(group.name(), phase, controls.cmode); } } if (group.isProductionGroup() && events.hasEvent(group.name(), ScheduleEvents::GROUP_PRODUCTION_UPDATE)) { const auto controls = group.productionControls(this->summary_state_); - this->groupState().production_control(group.name(), controls.cmode); + group_state.production_control(group.name(), controls.cmode); } if (group.has_gpmaint_control(Group::ProductionCMode::RESV)) { - this->groupState().production_control(group.name(), Group::ProductionCMode::RESV); + group_state.production_control(group.name(), Group::ProductionCMode::RESV); } for (Phase phase : all) { if (group.has_gpmaint_control(phase, Group::InjectionCMode::RATE)) { - this->groupState().injection_control(group.name(), phase, Group::InjectionCMode::RATE); + group_state.injection_control(group.name(), phase, Group::InjectionCMode::RATE); } else if (group.has_gpmaint_control(phase, Group::InjectionCMode::RESV)) { - this->groupState().injection_control(group.name(), phase, Group::InjectionCMode::RESV); + group_state.injection_control(group.name(), phase, Group::InjectionCMode::RESV); } } if (this->schedule_[this->report_step_].gconsale().has(group.name())) { - this->groupState().injection_control(group.name(), Phase::GAS, Group::InjectionCMode::SALE); + group_state.injection_control(group.name(), Phase::GAS, Group::InjectionCMode::SALE); } } diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index e528294dd1..324197b877 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -168,7 +168,7 @@ public: WellStateGuard pushWellState(WellState& well_state) { return WellStateGuard(*this, well_state); } - void setCmodeGroup(const Group& group); + void setCmodeGroup(GroupState& group_state, const Group& group) const; void setLogger(DeferredLogger* deferred_logger) { deferred_logger_ = deferred_logger; } template void setRegionAveragePressureCalculator( From 6f612e7d875929454ab7308b469b5cf6043f1433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Thu, 23 Oct 2025 09:31:15 +0200 Subject: [PATCH 13/26] Make some more update() methods const Make update() methods const by passing non-const GroupState --- .../wells/BlackoilWellModelGeneric.cpp | 18 +- .../wells/BlackoilWellModel_impl.hpp | 5 +- opm/simulators/wells/WellGroupHelper.cpp | 114 +- opm/simulators/wells/WellGroupHelper.hpp | 64 +- opm/simulators/wells/WellGroupHelpers.cpp | 1614 ----------------- 5 files changed, 119 insertions(+), 1696 deletions(-) delete mode 100644 opm/simulators/wells/WellGroupHelpers.cpp diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 1160096b7a..952647c9c0 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -1343,23 +1343,23 @@ updateAndCommunicateGroupData(const int reportStepIdx, constexpr std::array is_production_group = {true, false, false, false}; constexpr std::array phases = { Phase::OIL, Phase::WATER, Phase::OIL, Phase::GAS }; for (int i = 0; i < num_configs; i++) { - wg_helper.updateGroupControlledWells(is_production_group[i], phases[i]); + wg_helper.updateGroupControlledWells(is_production_group[i], phases[i], this->groupState()); } } // the group target reduction rates needs to be update since wells may have switched to/from GRUP control // The group target reduction does not honor NUPCOL. - wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/false); - wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/true); + wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/false, this->groupState()); + wg_helper.updateGroupTargetReduction(fieldGroup, /*is_injector=*/true, this->groupState()); { // Temporarily use the nupcol well state for all helper functions // At the end of this scope, the well state will be restored to its original value auto guard = wg_helper.pushWellState(this->nupcolWellState()); - wg_helper.updateREINForGroups(fieldGroup, /*sum_rank=*/comm_.rank() == 0); - wg_helper.updateVREPForGroups(fieldGroup); - wg_helper.updateReservoirRatesInjectionGroups(fieldGroup); - wg_helper.updateSurfaceRatesInjectionGroups(fieldGroup); - wg_helper.updateNetworkLeafNodeProductionRates(); - wg_helper.updateGroupProductionRates(fieldGroup); + wg_helper.updateREINForGroups(fieldGroup, /*sum_rank=*/comm_.rank() == 0, this->groupState()); + wg_helper.updateVREPForGroups(fieldGroup, this->groupState()); + wg_helper.updateReservoirRatesInjectionGroups(fieldGroup, this->groupState()); + wg_helper.updateSurfaceRatesInjectionGroups(fieldGroup, this->groupState()); + wg_helper.updateNetworkLeafNodeProductionRates(this->groupState()); + wg_helper.updateGroupProductionRates(fieldGroup, this->groupState()); } wg_helper.updateWellRates(fieldGroup, this->nupcolWellState()); this->wellState().communicateGroupRates(comm_); diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 10f4355e2d..3551d405be 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -301,7 +301,7 @@ namespace Opm { const auto& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx); - this->wgHelper().setCmodeGroup(this->groupState(), fieldGroup); + this->wgHelper().setCmodeGroup(fieldGroup, this->groupState()); // Define per region average pressure calculators for use by // pressure maintenance groups (GPMAINT keyword). @@ -481,7 +481,8 @@ namespace Opm { const Group& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx); this->wgHelper().updateGpMaintTargetForGroups(fieldGroup, regionalAveragePressureCalculator_, - dt); + dt, + this->groupState()); } this->updateAndCommunicateGroupData(reportStepIdx, diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index 3f99344661..4162349ebf 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -982,11 +982,11 @@ groupControlledWells(const std::string& group_name, template void WellGroupHelper:: -setCmodeGroup(GroupState& group_state, const Group& group) const +setCmodeGroup(const Group& group, GroupState& group_state) const { for (const std::string& group_name : group.groups()) { - this->setCmodeGroup(group_state, this->schedule_.getGroup(group_name, this->report_step_)); + this->setCmodeGroup(this->schedule_.getGroup(group_name, this->report_step_), group_state); } // use NONE as default control @@ -1198,21 +1198,25 @@ sumWellSurfaceRates(const Group& group, template int WellGroupHelper:: -updateGroupControlledWells(const bool is_production_group, const Phase injection_phase) +updateGroupControlledWells( + const bool is_production_group, const Phase injection_phase, GroupState& group_state +) const { OPM_TIMEFUNCTION(); const auto& group_name = "FIELD"; - return this->updateGroupControlledWellsRecursive_(group_name, is_production_group, injection_phase); + return this->updateGroupControlledWellsRecursive_( + group_name, is_production_group, injection_phase, group_state + ); } template void WellGroupHelper:: -updateGroupProductionRates(const Group& group) +updateGroupProductionRates(const Group& group, GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateGroupProductionRates(group_tmp); + this->updateGroupProductionRates(group_tmp, group_state); } const int np = this->wellState().numPhases(); std::vector rates(np, 0.0); @@ -1221,21 +1225,25 @@ updateGroupProductionRates(const Group& group) /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false ); } - this->groupState().update_production_rates(group.name(), rates); + group_state.update_production_rates(group.name(), rates); } template void WellGroupHelper:: -updateGroupTargetReduction(const Group& group, const bool is_injector) +updateGroupTargetReduction( + const Group& group, const bool is_injector, GroupState& group_state +) const { OPM_TIMEFUNCTION(); std::vector group_target_reduction(this->wellState().numPhases(), 0.0); - this->updateGroupTargetReductionRecursive_(group, is_injector, group_target_reduction); + this->updateGroupTargetReductionRecursive_( + group, is_injector, group_target_reduction, group_state + ); } template void WellGroupHelper:: -updateNetworkLeafNodeProductionRates() +updateNetworkLeafNodeProductionRates(GroupState& group_state) const { const auto& network = this->schedule_[this->report_step_].network(); if (network.active()) { @@ -1252,20 +1260,20 @@ updateNetworkLeafNodeProductionRates() } } } - this->groupState().update_network_leaf_node_production_rates(group_name, network_rates); + group_state.update_network_leaf_node_production_rates(group_name, network_rates); } } } template void WellGroupHelper:: -updateREINForGroups(const Group& group, bool sum_rank) +updateREINForGroups(const Group& group, bool sum_rank, GroupState& group_state) const { OPM_TIMEFUNCTION(); const int np = this->wellState().numPhases(); for (const std::string& groupName : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(groupName, this->report_step_); - this->updateREINForGroups(group_tmp, sum_rank); + this->updateREINForGroups(group_tmp, sum_rank, group_state); } std::vector rein(np, 0.0); @@ -1279,24 +1287,24 @@ updateREINForGroups(const Group& group, bool sum_rank) if (sum_rank) { const auto& pu = this->wellState().phaseUsageInfo(); if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - const auto& [consumption_rate, import_rate] = this->groupState().gconsump_rates(group.name()); + const auto& [consumption_rate, import_rate] = group_state.gconsump_rates(group.name()); const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); rein[ig] += import_rate; rein[ig] -= consumption_rate; } } - this->groupState().update_injection_rein_rates(group.name(), rein); + group_state.update_injection_rein_rates(group.name(), rein); } template void WellGroupHelper:: -updateReservoirRatesInjectionGroups(const Group& group) +updateReservoirRatesInjectionGroups(const Group& group, GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateReservoirRatesInjectionGroups(group_tmp); + this->updateReservoirRatesInjectionGroups(group_tmp, group_state); } const int np = this->wellState().numPhases(); std::vector resv(np, 0.0); @@ -1305,7 +1313,7 @@ updateReservoirRatesInjectionGroups(const Group& group) /*res_rates=*/true, group, phase, /*injector=*/true, /*network=*/false ); } - this->groupState().update_injection_reservoir_rates(group.name(), resv); + group_state.update_injection_reservoir_rates(group.name(), resv); } template @@ -1318,12 +1326,12 @@ updateState(WellState& well_state, GroupState& grou template void WellGroupHelper:: -updateSurfaceRatesInjectionGroups(const Group& group) +updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateSurfaceRatesInjectionGroups(group_tmp); + this->updateSurfaceRatesInjectionGroups(group_tmp, group_state); } const int np = this->wellState().numPhases(); std::vector rates(np, 0.0); @@ -1336,18 +1344,17 @@ updateSurfaceRatesInjectionGroups(const Group& group) /*network=*/false ); } - this->groupState().update_injection_surface_rates(group.name(), rates); + group_state.update_injection_surface_rates(group.name(), rates); } - template void WellGroupHelper:: -updateVREPForGroups(const Group& group) +updateVREPForGroups(const Group& group, GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateVREPForGroups(group_tmp); + this->updateVREPForGroups(group_tmp, group_state); } const int np = this->wellState().numPhases(); Scalar resv = 0.0; @@ -1356,7 +1363,7 @@ updateVREPForGroups(const Group& group) /*res_rates=*/true, group, phase, /*injector=*/false, /*network=*/false ); } - this->groupState().update_injection_vrep_rate(group.name(), resv); + group_state.update_injection_vrep_rate(group.name(), resv); } template @@ -1750,8 +1757,9 @@ int WellGroupHelper:: updateGroupControlledWellsRecursive_( const std::string& group_name, const bool is_production_group, - const Phase injection_phase -) + const Phase injection_phase, + GroupState& group_state +) const { const Group& group = this->schedule_.getGroup(group_name, this->report_step_); int num_wells = 0; @@ -1759,19 +1767,22 @@ updateGroupControlledWellsRecursive_( bool included = false; if (is_production_group) { - const auto ctrl = this->groupState().production_control(child_group); + const auto ctrl = group_state.production_control(child_group); included = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE); } else { - const auto ctrl = this->groupState().injection_control(child_group, injection_phase); + const auto ctrl = group_state.injection_control(child_group, injection_phase); included = included || (ctrl == Group::InjectionCMode::FLD) || (ctrl == Group::InjectionCMode::NONE); } if (included) { num_wells += this->updateGroupControlledWellsRecursive_( - child_group, is_production_group, injection_phase); + child_group, is_production_group, injection_phase, group_state + ); } else { - this->updateGroupControlledWellsRecursive_(child_group, is_production_group, injection_phase); + this->updateGroupControlledWellsRecursive_( + child_group, is_production_group, injection_phase, group_state + ); } } for (const std::string& child_well : group.wells()) { @@ -1789,7 +1800,7 @@ updateGroupControlledWellsRecursive_( included = included || this->wellState().isInjectionGrup(child_well); } } - const auto ctrl1 = this->groupState().production_control(group.name()); + const auto ctrl1 = group_state.production_control(group.name()); if (group.as_choke() && ((ctrl1 == Group::ProductionCMode::FLD) || (ctrl1 == Group::ProductionCMode::NONE))) { // The auto choke group has not own group control but inherits control from an ancestor group. @@ -1814,8 +1825,8 @@ updateGroupControlledWellsRecursive_( if (group_guide_rate > 0) { // Guide rate is not default for the auto choke group Scalar grat_target_from_sales = 0.0; - if (this->groupState().has_grat_sales_target(control_group_name)) - grat_target_from_sales = this->groupState().grat_sales_target(control_group_name); + if (group_state.has_grat_sales_target(control_group_name)) + grat_target_from_sales = group_state.grat_sales_target(control_group_name); std::vector resv_coeff(num_phases, 1.0); WGHelpers::TargetCalculator tcalc{ @@ -1824,7 +1835,7 @@ updateGroupControlledWellsRecursive_( resv_coeff, grat_target_from_sales, group.name(), - this->groupState(), + group_state, group.has_gpmaint_control(control_group_cmode) }; const auto& control_group_target = tcalc.groupTarget(ctrl, this->deferredLogger()); @@ -1851,9 +1862,9 @@ updateGroupControlledWellsRecursive_( } } if (is_production_group) { - this->groupState().update_number_of_wells_under_group_control(group_name, num_wells); + group_state.update_number_of_wells_under_group_control(group_name, num_wells); } else { - this->groupState().update_number_of_wells_under_inj_group_control( + group_state.update_number_of_wells_under_inj_group_control( group_name, injection_phase, num_wells ); } @@ -1864,13 +1875,19 @@ updateGroupControlledWellsRecursive_( template void WellGroupHelper:: updateGroupTargetReductionRecursive_( - const Group& group, const bool is_injector, std::vector& group_target_reduction) + const Group& group, + const bool is_injector, + std::vector& group_target_reduction, + GroupState& group_state +) const { const int np = this->wellState().numPhases(); for (const std::string& sub_group_name : group.groups()) { std::vector sub_group_target_reduction(np, 0.0); const Group& sub_group = this->schedule_.getGroup(sub_group_name, this->report_step_); - this->updateGroupTargetReductionRecursive_(sub_group, is_injector, sub_group_target_reduction); + this->updateGroupTargetReductionRecursive_( + sub_group, is_injector, sub_group_target_reduction, group_state + ); const Scalar sub_group_efficiency = sub_group.getGroupEfficiencyFactor(); @@ -1883,11 +1900,15 @@ updateGroupTargetReductionRecursive_( if (phase_pos == -1) continue; const Group::InjectionCMode& current_group_control - = this->groupState().injection_control(sub_group.name(), phase); + = group_state.injection_control(sub_group.name(), phase); const bool individual_control = (current_group_control != Group::InjectionCMode::FLD && current_group_control != Group::InjectionCMode::NONE); - const int num_group_controlled_wells - = this->groupControlledWells(sub_group.name(), "", !is_injector, phase); + const int num_group_controlled_wells = this->groupControlledWells( + sub_group.name(), + /*always_included_child=*/"", + !is_injector, + phase + ); if (individual_control || num_group_controlled_wells == 0) { group_target_reduction[phase_pos] += sub_group_efficiency * this->sumWellSurfaceRates(sub_group, phase_pos, is_injector); @@ -1903,11 +1924,10 @@ updateGroupTargetReductionRecursive_( } else { const Group::ProductionCMode& current_group_control = - this->groupState().production_control(sub_group.name()); + group_state.production_control(sub_group.name()); const bool individual_control = (current_group_control != Group::ProductionCMode::FLD && current_group_control != Group::ProductionCMode::NONE); - const int num_group_controlled_wells - = this->groupControlledWells( + const int num_group_controlled_wells = this->groupControlledWells( sub_group.name(), /*always_included_child=*/"", !is_injector, @@ -1975,10 +1995,10 @@ updateGroupTargetReductionRecursive_( } } if (is_injector) { - this->groupState().update_injection_reduction_rates(group.name(), group_target_reduction); + group_state.update_injection_reduction_rates(group.name(), group_target_reduction); } else { - this->groupState().update_production_reduction_rates(group.name(), group_target_reduction); + group_state.update_production_reduction_rates(group.name(), group_target_reduction); } } diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index 324197b877..aa78e507da 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -168,7 +168,7 @@ public: WellStateGuard pushWellState(WellState& well_state) { return WellStateGuard(*this, well_state); } - void setCmodeGroup(GroupState& group_state, const Group& group) const; + void setCmodeGroup(const Group& group, GroupState& group_state) const; void setLogger(DeferredLogger* deferred_logger) { deferred_logger_ = deferred_logger; } template void setRegionAveragePressureCalculator( @@ -188,19 +188,26 @@ public: const bool network = false ) const; template void updateGpMaintTargetForGroups( - const Group& group, const RegionalValues& regional_values, const double dt - ); - int updateGroupControlledWells(const bool is_production_group, const Phase injection_phase); - void updateGroupProductionRates(const Group& group); - void updateGroupTargetReduction(const Group& group, const bool is_injector); - void updateNetworkLeafNodeProductionRates(); - void updateREINForGroups(const Group& group, const bool sum_rank); - void updateReservoirRatesInjectionGroups(const Group& group); - void updateVREPForGroups(const Group& group); + const Group& group, + const RegionalValues& regional_values, + const double dt, + GroupState& group_state + ) const; + int updateGroupControlledWells( + const bool is_production_group, const Phase injection_phase, GroupState& group_state + ) const; + void updateGroupProductionRates(const Group& group, GroupState& group_state) const; + void updateGroupTargetReduction( + const Group& group, const bool is_injector, GroupState& group_state + ) const; + void updateNetworkLeafNodeProductionRates(GroupState& group_state) const; + void updateREINForGroups(const Group& group, bool sum_rank, GroupState& group_state) const; + void updateReservoirRatesInjectionGroups(const Group& group, GroupState& group_state) const; + void updateVREPForGroups(const Group& group, GroupState& group_state) const; void updateState( WellState& well_state, GroupState& group_state ); - void updateSurfaceRatesInjectionGroups(const Group& group); + void updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_state) const; void updateWellRates(const Group& group, const WellState& well_state_nupcol); const WellState& wellState() const { return *this->well_state_; } WellState& wellState() { return *this->well_state_; } @@ -229,10 +236,17 @@ private: ) const; std::optional selectRateComponent_(const int phase_pos) const; int updateGroupControlledWellsRecursive_( - const std::string& group_name, const bool is_production_group, const Phase injection_phase); + const std::string& group_name, + const bool is_production_group, + const Phase injection_phase, + GroupState& group_state + ) const; void updateGroupTargetReductionRecursive_( - const Group& group, const bool is_injector, std::vector& group_target_reduction); - + const Group& group, + const bool is_injector, + std::vector& group_target_reduction, + GroupState& group_state + ) const; WellState* well_state_{nullptr}; GroupState* group_state_{nullptr}; const Schedule& schedule_; @@ -292,12 +306,14 @@ template void WellGroupHelper:: updateGpMaintTargetForGroups(const Group& group, const RegionalValues& regional_values, - const double dt) + const double dt, + GroupState& group_state + ) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateGpMaintTargetForGroups(group_tmp, regional_values, dt); + this->updateGpMaintTargetForGroups(group_tmp, regional_values, dt, group_state); } const auto& gpm = group.gpmaint(); if (!gpm) @@ -325,7 +341,7 @@ updateGpMaintTargetForGroups(const Group& group, { if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - current_rate = this->groupState().injection_reservoir_rates(group.name())[io]; + current_rate = group_state.injection_reservoir_rates(group.name())[io]; } break; } @@ -333,7 +349,7 @@ updateGpMaintTargetForGroups(const Group& group, { if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - current_rate = this->groupState().injection_reservoir_rates(group.name())[iw]; + current_rate = group_state.injection_reservoir_rates(group.name())[iw]; } break; } @@ -341,7 +357,7 @@ updateGpMaintTargetForGroups(const Group& group, { if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - current_rate = this->groupState().injection_reservoir_rates(group.name())[ig]; + current_rate = group_state.injection_reservoir_rates(group.name())[ig]; } break; } @@ -349,7 +365,7 @@ updateGpMaintTargetForGroups(const Group& group, { if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - current_rate = this->groupState().injection_surface_rates(group.name())[io]; + current_rate = group_state.injection_surface_rates(group.name())[io]; } break; } @@ -357,7 +373,7 @@ updateGpMaintTargetForGroups(const Group& group, { if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - current_rate = this->groupState().injection_surface_rates(group.name())[iw]; + current_rate = group_state.injection_surface_rates(group.name())[iw]; } break; } @@ -365,14 +381,14 @@ updateGpMaintTargetForGroups(const Group& group, { if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - current_rate = this->groupState().injection_surface_rates(group.name())[ig]; + current_rate = group_state.injection_surface_rates(group.name())[ig]; } break; } default: throw std::invalid_argument("Invalid Flow target type in GPMAINT"); } - auto& gpmaint_state = this->groupState().gpmaint(group.name()); + auto& gpmaint_state = group_state.gpmaint(group.name()); // we only activate gpmaint if pressure is lower than the target regional pressure for injectors // (i.e. error > 0) and higher for producers. bool activate = (injection && error > 0) || (!injection && error < 0); @@ -382,7 +398,7 @@ updateGpMaintTargetForGroups(const Group& group, } else { gpm->resetState(gpmaint_state); } - this->groupState().update_gpmaint_target(group.name(), std::max(Scalar{0.0}, sign * rate)); + group_state.update_gpmaint_target(group.name(), std::max(Scalar{0.0}, sign * rate)); } } // namespace Opm diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp deleted file mode 100644 index 1b30dbe4d5..0000000000 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ /dev/null @@ -1,1614 +0,0 @@ -/* - Copyright 2019 Norce. - Copyright 2020 Equinor ASA. - - This file is part of the Open Porous Media project (OPM). - - OPM is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OPM is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OPM. If not, see . -*/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace { - template - Opm::GuideRate::RateVector - getGuideRateVector(const std::vector& rates, const Opm::PhaseUsageInfo& pu) - { - Scalar oilRate = 0.0; - if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { - oilRate = rates[pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx)]; - } - - Scalar gasRate = 0.0; - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - gasRate = rates[pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx)]; - } - - Scalar waterRate = 0.0; - if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { - waterRate = rates[pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx)]; - } - - return {oilRate, gasRate, waterRate}; - } - -} // namespace Anonymous - -namespace Opm { - - template - Scalar WellGroupHelpers:: - sumWellPhaseRates(bool res_rates, - const Opm::Group& group, - const Opm::Schedule& schedule, - const Opm::WellState& wellState, - const SummaryState& summaryState, - const int reportStepIdx, - const int phasePos, - const bool injector, - const bool network) - { - // Only obtain satellite rates once (on rank 0) - Scalar rate = 0.0; - if (wellState.isRank0() && (group.hasSatelliteProduction() || group.hasSatelliteInjection())) { - if (injector) { - rate = satelliteInjectionRate(schedule[reportStepIdx], group, wellState.phaseUsageInfo(), phasePos, res_rates); - } else { - const auto rateComp = selectRateComponent(wellState.phaseUsageInfo(), phasePos); - if (rateComp.has_value()) { - rate = satelliteProductionRate(summaryState, schedule[reportStepIdx], group, *rateComp, res_rates); - } - } - // Satellite groups have no sub groups/wells so we're done - return rate; - } - - for (const std::string& groupName : group.groups()) { - const auto& groupTmp = schedule.getGroup(groupName, reportStepIdx); - const auto& gefac = groupTmp.getGroupEfficiencyFactor(network); - rate += gefac * sumWellPhaseRates(res_rates, groupTmp, schedule, wellState, summaryState, reportStepIdx, phasePos, injector, network); - } - - for (const std::string& wellName : group.wells()) { - const auto well_index = wellState.index(wellName); - if (!well_index.has_value()) - continue; - - if (! wellState.wellIsOwned(well_index.value(), wellName) ) // Only sum once - { - continue; - } - - const auto& wellEcl = schedule.getWell(wellName, reportStepIdx); - // only count producers or injectors - if ((wellEcl.isProducer() && injector) || (wellEcl.isInjector() && !injector)) - continue; - - const auto& ws = wellState.well(well_index.value()); - if (ws.status == Opm::Well::Status::SHUT) - continue; - - const Scalar factor = wellEcl.getEfficiencyFactor(network) * wellState[wellEcl.name()].efficiency_scaling_factor; - if (res_rates) { - const auto& well_rates = ws.reservoir_rates; - if (injector) - rate += factor * well_rates[phasePos]; - else - rate -= factor * well_rates[phasePos]; - } else { - const auto& well_rates = ws.surface_rates; - if (injector) - rate += factor * well_rates[phasePos]; - else - rate -= factor * well_rates[phasePos]; - } - } - return rate; - } - -template -Scalar WellGroupHelpers:: -satelliteInjectionRate(const ScheduleState& sched, - const Group& group, - const PhaseUsageInfo& pu, - const int phase_pos, - bool res_rates) -{ - Scalar rate = 0.0; - if (group.hasSatelliteInjection()) { - std::optional ph; - for (const auto& [bo_phase, phase] : std::array { - std::pair( IndexTraits::waterPhaseIdx, Phase::WATER), - std::pair( IndexTraits::oilPhaseIdx, Phase::OIL), - std::pair( IndexTraits::gasPhaseIdx, Phase::GAS) }) - { - if (pu.phaseIsActive(bo_phase) && (pu.canonicalToActivePhaseIdx(bo_phase) == phase_pos)) { - ph = phase; - } - } - if (ph.has_value()) { - const auto& satellite_inj = sched.satelliteInjection(group.name()); - const auto& rate_ix = satellite_inj.rateIndex(ph.value()); - if (rate_ix.has_value()) { - const auto& satrates = satellite_inj[*rate_ix]; - if (!res_rates) { // surface rates - if (const auto& qs = satrates.surface(); qs.has_value()) { - rate = *qs; - } - // We don't support reservoir rates for satellite injection groups - } - } - } - } - return rate; -} - -template -Scalar WellGroupHelpers:: -satelliteProductionRate(const SummaryState& summaryState, - const ScheduleState& sched, - const Group& group, - const GSatProd::GSatProdGroupProp::Rate rateComp, - bool res_rates) -{ - Scalar rate = 0.0; - if (group.hasSatelliteProduction()) { - const auto& gsatProd = sched.gsatprod(); - if (!res_rates) { - rate = gsatProd.get(group.name(), summaryState).rate[rateComp]; - } - // We don't support reservoir rates for satellite production groups - } - return rate; -} - -template -std::optional -WellGroupHelpers:: -selectRateComponent(const PhaseUsageInfo& pu, const int phasePos) -{ - // TODO: this function can be wrong, phasePos is not used anymore, this function requries checking and refactoring. - using Rate = GSatProd::GSatProdGroupProp::Rate; - - for (const auto& [phase, rateComp] : std::array { - std::pair { IndexTraits::waterPhaseIdx, Rate::Water }, - std::pair { IndexTraits::oilPhaseIdx, Rate::Oil }, - std::pair { IndexTraits::gasPhaseIdx, Rate::Gas } }) - { - if (pu.phaseIsActive(phase)) { - const auto activePhasePos = pu.canonicalToActivePhaseIdx(phase); - if (activePhasePos == phasePos) { - return rateComp; - } - } - } - - return std::nullopt; -} - -template -void WellGroupHelpers:: -setCmodeGroup(const Group& group, - const Schedule& schedule, - const SummaryState& summaryState, - const int reportStepIdx, - GroupState& group_state) -{ - - for (const std::string& groupName : group.groups()) { - setCmodeGroup(schedule.getGroup(groupName, reportStepIdx), - schedule, summaryState, reportStepIdx, group_state); - } - - // use NONE as default control - const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; - for (Phase phase : all) { - if (!group_state.has_injection_control(group.name(), phase)) { - group_state.injection_control(group.name(), phase, Group::InjectionCMode::NONE); - } - } - if (!group_state.has_production_control(group.name())) { - group_state.production_control(group.name(), Group::ProductionCMode::NONE); - } - - const auto& events = schedule[reportStepIdx].wellgroup_events(); - if (group.isInjectionGroup() - && events.hasEvent(group.name(), ScheduleEvents::GROUP_INJECTION_UPDATE)) { - - for (Phase phase : all) { - if (!group.hasInjectionControl(phase)) - continue; - - const auto& controls = group.injectionControls(phase, summaryState); - group_state.injection_control(group.name(), phase, controls.cmode); - } - } - - if (group.isProductionGroup() - && events.hasEvent(group.name(), ScheduleEvents::GROUP_PRODUCTION_UPDATE)) { - const auto controls = group.productionControls(summaryState); - group_state.production_control(group.name(), controls.cmode); - } - - if (group.has_gpmaint_control(Group::ProductionCMode::RESV)) { - group_state.production_control(group.name(), Group::ProductionCMode::RESV); - } - for (Phase phase : all) { - if (group.has_gpmaint_control(phase, Group::InjectionCMode::RATE)) { - group_state.injection_control(group.name(), phase, Group::InjectionCMode::RATE); - } else if (group.has_gpmaint_control(phase, Group::InjectionCMode::RESV)) { - group_state.injection_control(group.name(), phase, Group::InjectionCMode::RESV); - } - } - - if (schedule[reportStepIdx].gconsale().has(group.name())) { - group_state.injection_control(group.name(), Phase::GAS, Group::InjectionCMode::SALE); - } -} - -template -void WellGroupHelpers:: -accumulateGroupEfficiencyFactor(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - Scalar& factor) -{ - factor *= group.getGroupEfficiencyFactor(); - if (group.parent() != "FIELD" && !group.parent().empty()) - accumulateGroupEfficiencyFactor( - schedule.getGroup(group.parent(), reportStepIdx), schedule, reportStepIdx, factor); -} - -template -Scalar -WellGroupHelpers:: -sumWellSurfaceRates(const Group& group, - const Schedule& schedule, - const WellState& wellState, - const int reportStepIdx, - const int phasePos, - const bool injector, - const SummaryState& summaryState) -{ - return sumWellPhaseRates(false, group, schedule, wellState, summaryState, reportStepIdx, phasePos, injector); -} - -template -Scalar -WellGroupHelpers:: -sumWellResRates(const Group& group, - const Schedule& schedule, - const WellState& wellState, - const int reportStepIdx, - const int phasePos, - const bool injector, - const SummaryState& summaryState) -{ - return sumWellPhaseRates(true, group, schedule, wellState, summaryState, reportStepIdx, phasePos, injector); -} - -template -Scalar -WellGroupHelpers:: -sumSolventRates(const Group& group, - const Schedule& schedule, - const WellState& wellState, - const int reportStepIdx, - const bool injector) -{ - Scalar rate = 0.0; - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - const auto& gefac = groupTmp.getGroupEfficiencyFactor(); - rate += gefac * sumSolventRates(groupTmp, schedule, wellState, reportStepIdx, injector); - } - - for (const std::string& wellName : group.wells()) { - const auto well_index = wellState.index(wellName); - if (!well_index.has_value()) - continue; - - if (! wellState.wellIsOwned(well_index.value(), wellName) ) // Only sum once - { - continue; - } - - const auto& wellEcl = schedule.getWell(wellName, reportStepIdx); - // only count producers or injectors - if ((wellEcl.isProducer() && injector) || (wellEcl.isInjector() && !injector)) - continue; - - const auto& ws = wellState.well(well_index.value()); - - if (ws.status == Well::Status::SHUT) - continue; - - const Scalar factor = wellEcl.getEfficiencyFactor() * - wellState[wellEcl.name()].efficiency_scaling_factor; - if (injector) - rate += factor * ws.sum_solvent_rates(); - else - rate -= factor * ws.sum_solvent_rates(); - } - return rate; -} - -template -void WellGroupHelpers:: -updateGroupTargetReduction(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const bool isInjector, - const GuideRate& guide_rate, - const WellState& wellState, - const SummaryState& summaryState, - GroupState& group_state, - std::vector& groupTargetReduction) -{ - OPM_TIMEFUNCTION(); - const int np = wellState.numPhases(); - for (const std::string& subGroupName : group.groups()) { - std::vector subGroupTargetReduction(np, 0.0); - const Group& subGroup = schedule.getGroup(subGroupName, reportStepIdx); - updateGroupTargetReduction(subGroup, - schedule, - reportStepIdx, - isInjector, - guide_rate, - wellState, - summaryState, - group_state, - subGroupTargetReduction); - - const Scalar subGroupEfficiency = subGroup.getGroupEfficiencyFactor(); - - // accumulate group contribution from sub group - if (isInjector) { - const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; - for (Phase phase : all) { - - const auto& pu = wellState.phaseUsageInfo(); - int phase_pos = -1; - switch (phase) { - case Phase::GAS: - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - phase_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - } - break; - case Phase::OIL: - if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { - phase_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - } - break; - case Phase::WATER: - if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { - phase_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - } - break; - default: - // just to avoid warning - throw std::invalid_argument("unhandled phase enum"); - } - - // the phase is not present - if (phase_pos == -1) - continue; - - const Group::InjectionCMode& currentGroupControl - = group_state.injection_control(subGroup.name(), phase); - const bool individual_control = (currentGroupControl != Group::InjectionCMode::FLD - && currentGroupControl != Group::InjectionCMode::NONE); - const int num_group_controlled_wells - = groupControlledWells(schedule, wellState, group_state, reportStepIdx, subGroupName, "", !isInjector, phase); - if (individual_control || num_group_controlled_wells == 0) { - groupTargetReduction[phase_pos] - += subGroupEfficiency * sumWellSurfaceRates(subGroup, schedule, wellState, reportStepIdx, phase_pos, isInjector, summaryState); - } else { - // Accumulate from this subgroup only if no group guide rate is set for it. - if (!guide_rate.has(subGroupName, phase)) { - groupTargetReduction[phase_pos] += subGroupEfficiency * subGroupTargetReduction[phase_pos]; - } - } - } - } else { - const Group::ProductionCMode& currentGroupControl = group_state.production_control(subGroupName); - const bool individual_control = (currentGroupControl != Group::ProductionCMode::FLD - && currentGroupControl != Group::ProductionCMode::NONE); - const int num_group_controlled_wells - = groupControlledWells(schedule, wellState, group_state, reportStepIdx, subGroupName, "", !isInjector, /*injectionPhaseNotUsed*/Phase::OIL); - if (individual_control || num_group_controlled_wells == 0) { - for (int phase = 0; phase < np; phase++) { - groupTargetReduction[phase] - += subGroupEfficiency * sumWellSurfaceRates(subGroup, schedule, wellState, reportStepIdx, phase, isInjector, summaryState); - } - } else { - // The subgroup may participate in group control. - if (!guide_rate.has(subGroupName)) { - // Accumulate from this subgroup only if no group guide rate is set for it. - for (int phase = 0; phase < np; phase++) { - groupTargetReduction[phase] += subGroupEfficiency * subGroupTargetReduction[phase]; - } - } - } - } - } - - for (const std::string& wellName : group.wells()) { - const auto& wellTmp = schedule.getWell(wellName, reportStepIdx); - - if (wellTmp.isProducer() && isInjector) - continue; - - if (wellTmp.isInjector() && !isInjector) - continue; - - const auto well_index = wellState.index(wellName); - if (!well_index.has_value()) - continue; - - if (! wellState.wellIsOwned(well_index.value(), wellName) ) // Only sum once - { - continue; - } - - const auto& ws = wellState.well(well_index.value()); - if (ws.status == Well::Status::SHUT) - continue; - - const Scalar efficiency = wellTmp.getEfficiencyFactor() * - wellState[wellTmp.name()].efficiency_scaling_factor; - - // add contribution from wells not under group control - if (isInjector) { - if (ws.injection_cmode != Well::InjectorCMode::GRUP) - for (int phase = 0; phase < np; phase++) { - groupTargetReduction[phase] += ws.surface_rates[phase] * efficiency; - } - } else { - if ((ws.production_cmode != Well::ProducerCMode::GRUP)){ - if (!group.as_choke()) { - for (int phase = 0; phase < np; phase++) { - groupTargetReduction[phase] -= ws.surface_rates[phase] * efficiency; - } - } - } - } - } - if (isInjector) - group_state.update_injection_reduction_rates(group.name(), groupTargetReduction); - else - group_state.update_production_reduction_rates(group.name(), groupTargetReduction); -} - -template -void WellGroupHelpers:: -updateWellRatesFromGroupTargetScale(const Scalar scale, - const Group& group, - const Schedule& schedule, - const int reportStepIdx, - bool isInjector, - const GroupState& group_state, - WellState& wellState) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - bool individual_control = false; - if (isInjector) { - const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; - for (Phase phase : all) { - const Group::InjectionCMode& currentGroupControl - = group_state.injection_control(groupName, phase); - individual_control = individual_control || (currentGroupControl != Group::InjectionCMode::FLD - && currentGroupControl != Group::InjectionCMode::NONE); - } - } else { - const Group::ProductionCMode& currentGroupControl = group_state.production_control(groupName); - individual_control = (currentGroupControl != Group::ProductionCMode::FLD - && currentGroupControl != Group::ProductionCMode::NONE); - } - if (!individual_control) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateWellRatesFromGroupTargetScale(scale, groupTmp, schedule, reportStepIdx, isInjector, group_state, wellState); - } - } - - const int np = wellState.numPhases(); - for (const std::string& wellName : group.wells()) { - const auto& wellTmp = schedule.getWell(wellName, reportStepIdx); - - if (wellTmp.isProducer() && isInjector) - continue; - - if (wellTmp.isInjector() && !isInjector) - continue; - - const auto well_index = wellState.index(wellName); - if (!well_index.has_value()) - continue; - - auto& ws = wellState.well(well_index.value()); - if (ws.status == Well::Status::SHUT) - continue; - - // scale rates - if (isInjector) { - if (ws.injection_cmode == Well::InjectorCMode::GRUP) - for (int phase = 0; phase < np; phase++) { - ws.surface_rates[phase] *= scale; - } - } else { - if (ws.production_cmode == Well::ProducerCMode::GRUP) { - for (int phase = 0; phase < np; phase++) { - ws.surface_rates[phase] *= scale; - } - } - } - } - - -} - -template -void WellGroupHelpers:: -updateVREPForGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellState& wellState, - GroupState& group_state, - const SummaryState& summaryState) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateVREPForGroups(groupTmp, schedule, reportStepIdx, wellState, group_state, summaryState); - } - const int np = wellState.numPhases(); - Scalar resv = 0.0; - for (int phase = 0; phase < np; ++phase) { - resv += sumWellPhaseRates(true, - group, - schedule, - wellState, - summaryState, - reportStepIdx, - phase, - /*isInjector*/ false); - } - group_state.update_injection_vrep_rate(group.name(), resv); -} - -template -void WellGroupHelpers:: -updateReservoirRatesInjectionGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellState& wellState, - GroupState& group_state, - const SummaryState& summaryState) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateReservoirRatesInjectionGroups(groupTmp, schedule, reportStepIdx, wellState, group_state, summaryState); - } - const int np = wellState.numPhases(); - std::vector resv(np, 0.0); - for (int phase = 0; phase < np; ++phase) { - resv[phase] = sumWellPhaseRates(true, - group, - schedule, - wellState, - summaryState, - reportStepIdx, - phase, - /*isInjector*/ true); - } - group_state.update_injection_reservoir_rates(group.name(), resv); -} - -template -void WellGroupHelpers:: -updateSurfaceRatesInjectionGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellState& wellState, - GroupState& group_state, - const SummaryState& summaryState) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateSurfaceRatesInjectionGroups(groupTmp, schedule, reportStepIdx, wellState, group_state, summaryState); - } - const int np = wellState.numPhases(); - std::vector rates(np, 0.0); - for (int phase = 0; phase < np; ++phase) { - rates[phase] = sumWellPhaseRates(false, - group, - schedule, - wellState, - summaryState, - reportStepIdx, - phase, - /*isInjector*/ true); - } - group_state.update_injection_surface_rates(group.name(), rates); -} - -template -void WellGroupHelpers:: -updateWellRates(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellState& wellStateNupcol, - WellState& wellState) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateWellRates(groupTmp, schedule, reportStepIdx, wellStateNupcol, wellState); - } - const int np = wellState.numPhases(); - for (const std::string& wellName : group.wells()) { - std::vector rates(np, 0.0); - const auto well_index = wellState.index(wellName); - if (well_index.has_value()) { // the well is found on this node - const auto& wellTmp = schedule.getWell(wellName, reportStepIdx); - int sign = 1; - // production wellRates are negative. The users of currentWellRates uses the convention in - // opm-common that production and injection rates are positive. - if (!wellTmp.isInjector()) - sign = -1; - const auto& ws = wellStateNupcol.well(well_index.value()); - for (int phase = 0; phase < np; ++phase) { - rates[phase] = sign * ws.surface_rates[phase]; - } - } - wellState.setCurrentWellRates(wellName, rates); - } -} - -template -void WellGroupHelpers:: -updateGroupProductionRates(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const WellState& wellState, - GroupState& group_state, - const SummaryState& summaryState) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateGroupProductionRates(groupTmp, schedule, reportStepIdx, wellState, group_state, summaryState); - } - const int np = wellState.numPhases(); - std::vector rates(np, 0.0); - for (int phase = 0; phase < np; ++phase) { - rates[phase] = sumWellPhaseRates(false, group, schedule, wellState, summaryState, reportStepIdx, phase, /*isInjector*/ false); - } - group_state.update_production_rates(group.name(), rates); -} - -template -void WellGroupHelpers:: -updateNetworkLeafNodeProductionRates(const Schedule& schedule, - const int reportStepIdx, - const WellState& wellState, - GroupState& group_state, - const SummaryState& summaryState) -{ - const auto& network = schedule[reportStepIdx].network(); - if (network.active()) { - const int np = wellState.numPhases(); - for (const auto& group_name : network.leaf_nodes()) { - std::vector network_rates(np, 0.0); - if (schedule[reportStepIdx].groups.has(group_name)) { // Allow empty leaf nodes that are not groups - const auto& group = schedule[reportStepIdx].groups.get(group_name); - if (group.numWells() > 0) { - for (int phase = 0; phase < np; ++phase) { - network_rates[phase] = sumWellPhaseRates(false, group, schedule, wellState, summaryState, reportStepIdx, phase, /*isInjector*/ false, /*network*/ true); - } - } - } - group_state.update_network_leaf_node_production_rates(group_name, network_rates); - } - } -} - -template -void WellGroupHelpers:: -updateREINForGroups(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const SummaryState& st, - const WellState& wellState, - GroupState& group_state, - bool sum_rank) -{ - OPM_TIMEFUNCTION(); - const int np = wellState.numPhases(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateREINForGroups(groupTmp, schedule, reportStepIdx, st, wellState, group_state, sum_rank); - } - - std::vector rein(np, 0.0); - for (int phase = 0; phase < np; ++phase) { - rein[phase] = sumWellPhaseRates(false, group, schedule, wellState, st, reportStepIdx, phase, /*isInjector*/ false); - } - - // add import rate and subtract consumption rate for group for gas - if (sum_rank) { - const auto& pu = wellState.phaseUsageInfo(); - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - const auto& [consumption_rate, import_rate] = group_state.gconsump_rates(group.name()); - const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - rein[ig] += import_rate; - rein[ig] -= consumption_rate; - } - } - - group_state.update_injection_rein_rates(group.name(), rein); -} - -template -template -void WellGroupHelpers:: -updateGpMaintTargetForGroups(const Group& group, - const Schedule& schedule, - const RegionalValues& regional_values, - const int reportStepIdx, - const double dt, - const WellState& well_state, - GroupState& group_state) -{ - OPM_TIMEFUNCTION(); - for (const std::string& groupName : group.groups()) { - const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateGpMaintTargetForGroups(groupTmp, schedule, regional_values, reportStepIdx, dt, well_state, group_state); - } - const auto& gpm = group.gpmaint(); - if (!gpm) - return; - - const auto& region = gpm->region(); - if (!region) - return; - - const auto [name, number] = *region; - const Scalar error = gpm->pressure_target() - regional_values.at(name)->pressure(number); - Scalar current_rate = 0.0; - const auto& pu = well_state.phaseUsageInfo(); - bool injection = true; - Scalar sign = 1.0; - switch (gpm->flow_target()) { - case GPMaint::FlowTarget::RESV_PROD: - { - current_rate = -group_state.injection_vrep_rate(group.name()); - injection = false; - sign = -1.0; - break; - } - case GPMaint::FlowTarget::RESV_OINJ: - { - if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { - const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - current_rate = group_state.injection_reservoir_rates(group.name())[io]; - } - break; - } - case GPMaint::FlowTarget::RESV_WINJ: - { - if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { - const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - current_rate = group_state.injection_reservoir_rates(group.name())[iw]; - } - break; - } - case GPMaint::FlowTarget::RESV_GINJ: - { - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - current_rate = group_state.injection_reservoir_rates(group.name())[ig]; - } - break; - } - case GPMaint::FlowTarget::SURF_OINJ: - { - if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { - const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - current_rate = group_state.injection_surface_rates(group.name())[io]; - } - break; - } - case GPMaint::FlowTarget::SURF_WINJ: - { - if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { - const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - current_rate = group_state.injection_surface_rates(group.name())[iw]; - } - break; - } - case GPMaint::FlowTarget::SURF_GINJ: - { - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - current_rate = group_state.injection_surface_rates(group.name())[ig]; - } - break; - } - default: - throw std::invalid_argument("Invalid Flow target type in GPMAINT"); - } - auto& gpmaint_state = group_state.gpmaint(group.name()); - // we only activate gpmaint if pressure is lower than the target regional pressure for injectors - // (i.e. error > 0) and higher for producers. - bool activate = (injection && error > 0) || (!injection && error < 0); - Scalar rate = 0.0; - if (activate) { - rate = gpm->rate(gpmaint_state, current_rate, error, dt); - } else { - gpm->resetState(gpmaint_state); - } - group_state.update_gpmaint_target(group.name(), std::max(Scalar{0.0}, sign * rate)); -} - -template -std::map -WellGroupHelpers:: -computeNetworkPressures(const Network::ExtNetwork& network, - const WellStateType& well_state, - const GroupState& group_state, - const VFPProdProperties& vfp_prod_props, - const Schedule& schedule, - const Parallel::Communication& comm, - const int report_time_step) -{ - // TODO: Only dealing with production networks for now. - OPM_TIMEFUNCTION(); - if (!network.active()) { - return {}; - } - - std::map node_pressures; - auto roots = network.roots(); - for (const auto& root : roots) { - // Fixed pressure nodes of the network are the roots of trees. - // Leaf nodes must correspond to groups in the group structure. - // Let us first find all leaf nodes of the network. We also - // create a vector of all nodes, ordered so that a child is - // always after its parent. - std::stack children; - std::set leaf_nodes; - std::vector root_to_child_nodes; - //children.push(network.root().name()); - children.push(root.get().name()); - while (!children.empty()) { - const auto node = children.top(); - children.pop(); - root_to_child_nodes.push_back(node); - auto branches = network.downtree_branches(node); - if (branches.empty()) { - leaf_nodes.insert(node); - } - for (const auto& branch : branches) { - children.push(branch.downtree_node()); - } - } - assert(children.empty()); - - // Starting with the leaf nodes of the network, get the flow rates - // from the corresponding groups. - std::map> node_inflows; - const std::vector zero_rates(3, 0.0); - for (const auto& node : leaf_nodes) { - // Guard against empty leaf nodes (may not be present in GRUPTREE) - if (!group_state.has_production_rates(node)) { - node_inflows[node] = zero_rates; - continue; - } - - node_inflows[node] = group_state.network_leaf_node_production_rates(node); - // Add the ALQ amounts to the gas rates if requested. - if (network.node(node).add_gas_lift_gas()) { - const auto& group = schedule.getGroup(node, report_time_step); - Scalar alq = 0.0; - for (const std::string& wellname : group.wells()) { - const Well& well = schedule.getWell(wellname, report_time_step); - if (well.isInjector() || !well_state.isOpen(wellname)) continue; - const Scalar efficiency = well.getEfficiencyFactor(/*network*/ true) * well_state.getGlobalEfficiencyScalingFactor(wellname); - const auto& well_index = well_state.index(wellname); - if (well_index.has_value() && well_state.wellIsOwned(well_index.value(), wellname)) { - alq += well_state.well(wellname).alq_state.get() * efficiency; - } - } - alq = comm.sum(alq); - node_inflows[node][IndexTraits::gasPhaseIdx] += alq; - } - } - - // Accumulate in the network, towards the roots. Note that a - // root (i.e. fixed pressure node) can still be contributing - // flow towards other nodes in the network, i.e. a node is - // the root of a subtree. - auto child_to_root_nodes = root_to_child_nodes; - std::reverse(child_to_root_nodes.begin(), child_to_root_nodes.end()); - for (const auto& node : child_to_root_nodes) { - const auto upbranch = network.uptree_branch(node); - if (upbranch) { - // Add downbranch rates to upbranch. - std::vector& up = node_inflows[(*upbranch).uptree_node()]; - const std::vector& down = node_inflows[node]; - // We now also support NEFAC - const Scalar efficiency = network.node(node).efficiency(); - if (up.empty()) { - up = std::vector(down.size(), 0.0); - } - assert (up.size() == down.size()); - for (std::size_t ii = 0; ii < up.size(); ++ii) { - up[ii] += efficiency*down[ii]; - } - } - } - - // Going the other way (from roots to leafs), calculate the pressure - // at each node using VFP tables and rates. - //std::map node_pressures; - for (const auto& node : root_to_child_nodes) { - auto press = network.node(node).terminal_pressure(); - if (press) { - node_pressures[node] = *press; - } else { - const auto upbranch = network.uptree_branch(node); - assert(upbranch); - const Scalar up_press = node_pressures[(*upbranch).uptree_node()]; - const auto vfp_table = (*upbranch).vfp_table(); - if (vfp_table) { - OPM_TIMEBLOCK(NetworkVfpCalculations); - // The rates are here positive, but the VFP code expects the - // convention that production rates are negative, so we must - // take a copy and flip signs. - auto rates = node_inflows[node]; - std::transform(rates.begin(), rates.end(), rates.begin(), - [](const auto r) { return -r; }); - assert(rates.size() == 3); - // NB! ALQ in extended network is never implicitly the gas lift rate (GRAT), i.e., the - // gas lift rates only enters the network pressure calculations through the rates - // (e.g., in GOR calculations) unless a branch ALQ is set in BRANPROP. - // - // @TODO: Standard network - Scalar alq = (*upbranch).alq_value().value_or(0.0); - node_pressures[node] = vfp_prod_props.bhp(*vfp_table, - rates[IndexTraits::waterPhaseIdx], - rates[IndexTraits::oilPhaseIdx], - rates[IndexTraits::gasPhaseIdx], - up_press, - alq, - 0.0, //explicit_wfr - 0.0, //explicit_gfr - false); //use_expvfp we dont support explicit lookup -#define EXTRA_DEBUG_NETWORK 0 -#if EXTRA_DEBUG_NETWORK - std::ostringstream oss; - oss << "parent: " << (*upbranch).uptree_node() << " child: " << node - << " rates = [ " << rates[0]*86400 << ", " << rates[1]*86400 << ", " << rates[2]*86400 << " ]" - << " p(parent) = " << up_press/1e5 << " p(child) = " << node_pressures[node]/1e5 << std::endl; - OpmLog::debug(oss.str()); -#endif - } else { - // Table number specified as 9999 in the deck, no pressure loss. - if (network.node(node).as_choke()){ - // Node pressure is set to the group THP. - node_pressures[node] = group_state.well_group_thp(node); - } else { - node_pressures[node] = up_press; - } - } - } - } - } - return node_pressures; -} - -template -GuideRate::RateVector -WellGroupHelpers:: -getWellRateVector(const WellState& well_state, - const std::string& name) -{ - return getGuideRateVector(well_state.currentWellRates(name), well_state.phaseUsageInfo()); -} - -template -GuideRate::RateVector -WellGroupHelpers:: -getProductionGroupRateVector(const GroupState& group_state, - const PhaseUsageInfo& pu, - const std::string& group_name) -{ - return getGuideRateVector(group_state.production_rates(group_name), pu); -} - -template -Scalar -WellGroupHelpers:: -getGuideRate(const std::string& name, - const Schedule& schedule, - const WellState& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const GuideRateModel::Target target) -{ - if (schedule.hasWell(name, reportStepIdx)) { - if (guideRate->has(name) || guideRate->hasPotentials(name)) { - return guideRate->get(name, target, getWellRateVector(wellState, name)); - } else { - return 0.0; - } - } - - if (guideRate->has(name)) { - return guideRate->get(name, target, getProductionGroupRateVector(group_state, wellState.phaseUsageInfo(), name)); - } - - Scalar totalGuideRate = 0.0; - const Group& group = schedule.getGroup(name, reportStepIdx); - - for (const std::string& groupName : group.groups()) { - const Group::ProductionCMode& currentGroupControl = group_state.production_control(groupName); - if (currentGroupControl == Group::ProductionCMode::FLD - || currentGroupControl == Group::ProductionCMode::NONE) { - // accumulate from sub wells/groups - totalGuideRate += getGuideRate(groupName, schedule, wellState, group_state, reportStepIdx, guideRate, target); - } - } - - for (const std::string& wellName : group.wells()) { - const auto& wellTmp = schedule.getWell(wellName, reportStepIdx); - - if (wellTmp.isInjector()) - continue; - - const auto well_index = wellState.index(wellName); - if (!well_index.has_value()) - continue; - - if (! wellState.wellIsOwned(well_index.value(), wellName) ) // Only sum once - { - continue; - } - - const auto& ws = wellState.well(well_index.value()); - if (ws.status == Well::Status::SHUT) - continue; - - // Only count wells under group control or the ru - if (!wellState.isProductionGrup(wellName)) - continue; - - totalGuideRate += getGuideRate(wellName, schedule, wellState, group_state, - reportStepIdx, guideRate, target); - - } - return totalGuideRate; -} - -template -Scalar -WellGroupHelpers:: -getGuideRateInj(const std::string& name, - const Schedule& schedule, - const WellState& wellState, - const GroupState& group_state, - const int reportStepIdx, - const GuideRate* guideRate, - const GuideRateModel::Target target, - const Phase& injectionPhase) -{ - if (schedule.hasWell(name, reportStepIdx)) { - return getGuideRate(name, schedule, wellState, group_state, - reportStepIdx, guideRate, target); - } - - if (guideRate->has(name, injectionPhase)) { - return guideRate->get(name, injectionPhase); - } - - Scalar totalGuideRate = 0.0; - const Group& group = schedule.getGroup(name, reportStepIdx); - - for (const std::string& groupName : group.groups()) { - const Group::InjectionCMode& currentGroupControl - = group_state.injection_control(groupName, injectionPhase); - if (currentGroupControl == Group::InjectionCMode::FLD - || currentGroupControl == Group::InjectionCMode::NONE) { - // accumulate from sub wells/groups - totalGuideRate += getGuideRateInj(groupName, schedule, wellState, group_state, reportStepIdx, guideRate, target, injectionPhase); - } - } - - for (const std::string& wellName : group.wells()) { - const auto& wellTmp = schedule.getWell(wellName, reportStepIdx); - - if (!wellTmp.isInjector()) - continue; - - const auto well_index = wellState.index(wellName); - if (!well_index.has_value()) - continue; - - if (! wellState.wellIsOwned(well_index.value(), wellName) ) // Only sum once - { - continue; - } - - const auto& ws = wellState.well(well_index.value()); - if (ws.status == Well::Status::SHUT) - continue; - - // Only count wells under group control or the ru - if (!wellState.isInjectionGrup(wellName)) - continue; - - totalGuideRate += guideRate->get(wellName, target, getWellRateVector(wellState, wellName)); - } - return totalGuideRate; -} - -template -int WellGroupHelpers:: -updateGroupControlledWells(const Schedule& schedule, - const WellStateType& well_state, - GroupState& group_state, - const SummaryState& summary_state, - const GuideRate* guideRate, - const int report_step, - const std::string& group_name, - const bool is_production_group, - const Phase injection_phase) -{ - OPM_TIMEFUNCTION(); - const Group& group = schedule.getGroup(group_name, report_step); - int num_wells = 0; - for (const std::string& child_group : group.groups()) { - - bool included = false; - if (is_production_group) { - const auto ctrl = group_state.production_control(child_group); - included = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE); - } else { - const auto ctrl = group_state.injection_control(child_group, injection_phase); - included = included || (ctrl == Group::InjectionCMode::FLD) || (ctrl == Group::InjectionCMode::NONE); - } - - if (included) { - num_wells - += updateGroupControlledWells(schedule, well_state, group_state, summary_state, guideRate, report_step, child_group, is_production_group, injection_phase); - } else { - updateGroupControlledWells(schedule, well_state, group_state, summary_state, guideRate, report_step, child_group, is_production_group, injection_phase); - } - } - for (const std::string& child_well : group.wells()) { - bool included = false; - const Well& well = schedule.getWell(child_well, report_step); - if (is_production_group && well.isProducer()) { - included = included || well_state.isProductionGrup(child_well) || group.as_choke(); - } else if (!is_production_group && !well.isProducer()) { - const auto& well_controls = well.injectionControls(summary_state); - auto injectorType = well_controls.injector_type; - if ( (injection_phase == Phase::WATER && injectorType == InjectorType::WATER ) || - (injection_phase == Phase::OIL && injectorType == InjectorType::OIL ) || - (injection_phase == Phase::GAS && injectorType == InjectorType::GAS )) - { - included = included || well_state.isInjectionGrup(child_well); - } - } - const auto ctrl1 = group_state.production_control(group.name()); - if (group.as_choke() && ((ctrl1 == Group::ProductionCMode::FLD) || (ctrl1 == Group::ProductionCMode::NONE))){ - // The auto choke group has not own group control but inherits control from an ancestor group. - // Number of wells should be calculated as zero when wells of auto choke group do not deliver target. - // This behaviour is then similar to no-autochoke group with wells not on GRUP control. - // The rates of these wells are summed up. The parent group target is reduced with this rate. - // This reduced target becomes the target of the other child group of this parent. - const auto num_phases = well_state.numPhases(); - std::vector rates(num_phases, 0.0); - for (int phase_pos = 0; phase_pos < num_phases; ++phase_pos) { - rates[phase_pos] = WellGroupHelpers::sumWellSurfaceRates(group, - schedule, - well_state, - report_step, - phase_pos, - false, - summary_state); - } - - // Get the ancestor of the auto choke group that has group control (cmode != FLD, NONE) - const auto& control_group_name = control_group(group, group_state, report_step, schedule); - const auto& control_group = schedule.getGroup(control_group_name, report_step); - const auto& ctrl = control_group.productionControls(summary_state); - const auto& control_group_cmode = ctrl.cmode; - - const auto& group_guide_rate = group.productionControls(summary_state).guide_rate; - - if (group_guide_rate > 0) { - // Guide rate is not default for the auto choke group - Scalar gratTargetFromSales = 0.0; - if (group_state.has_grat_sales_target(control_group_name)) - gratTargetFromSales = group_state.grat_sales_target(control_group_name); - - std::vector resv_coeff(num_phases, 1.0); - WGHelpers::TargetCalculator tcalc(control_group_cmode, - well_state.phaseUsageInfo(), - resv_coeff, - gratTargetFromSales, - group.name(), - group_state, - group.has_gpmaint_control(control_group_cmode)); - auto deferred_logger = Opm::DeferredLogger(); - const auto& control_group_target = tcalc.groupTarget(ctrl, deferred_logger); - - // Calculates the guide rate of the parent group with control. - // It is allowed that the guide rate of this group is defaulted. The guide rate will be derived from the children groups - const auto& control_group_guide_rate = getGuideRate(control_group_name, - schedule, - well_state, - group_state, - report_step, - guideRate, - tcalc.guideTargetMode()); - - if (control_group_guide_rate > 0) { - // Target rate for the auto choke group - const Scalar target_rate = control_group_target * group_guide_rate / control_group_guide_rate; - const Scalar current_rate = tcalc.calcModeRateFromRates(rates); - - if (current_rate < target_rate) - included = false; - } - } - } - - if (included) { - ++num_wells; - } - } - if (is_production_group) { - group_state.update_number_of_wells_under_group_control(group_name, num_wells); - } else { - group_state.update_number_of_wells_under_inj_group_control(group_name, injection_phase, num_wells); - } - - return num_wells; -} - - -template -int WellGroupHelpers:: -groupControlledWells(const Schedule& schedule, - const WellStateType& well_state, - const GroupState& group_state, - const int report_step, - const std::string& group_name, - const std::string& always_included_child, - const bool is_production_group, - const Phase injection_phase) -{ - auto num_wells = is_production_group ? group_state.number_of_wells_under_group_control(group_name) - : group_state.number_of_wells_under_inj_group_control(group_name, injection_phase); - if (schedule.hasWell(always_included_child, report_step)) { - const bool isInGroup = isInGroupChainTopBot(always_included_child, group_name, schedule, report_step); - const bool already_included = is_production_group ? well_state.isProductionGrup(always_included_child) - : well_state.isInjectionGrup(always_included_child); - if (!already_included && isInGroup) { - num_wells++; - } - } - return num_wells; -} - -template -std::vector -WellGroupHelpers:: -groupChainTopBot(const std::string& bottom, - const std::string& top, - const Schedule& schedule, - const int report_step) -{ - // Get initial parent, 'bottom' can be a well or a group. - std::string parent; - if (schedule.hasWell(bottom, report_step)) { - parent = schedule.getWell(bottom, report_step).groupName(); - } else { - parent = schedule.getGroup(bottom, report_step).parent(); - } - - // Build the chain from bottom to top. - std::vector chain; - chain.push_back(bottom); - chain.push_back(parent); - while (parent != top) { - parent = schedule.getGroup(parent, report_step).parent(); - chain.push_back(parent); - } - assert(chain.back() == top); - - // Reverse order and return. - std::reverse(chain.begin(), chain.end()); - return chain; -} - -template -bool -WellGroupHelpers:: -isInGroupChainTopBot(const std::string& bottom, - const std::string& top, - const Schedule& schedule, - const int report_step) -{ - // Get initial parent, 'bottom' can be a well or a group. - std::string parent; - if (schedule.hasWell(bottom, report_step)) { - parent = schedule.getWell(bottom, report_step).groupName(); - } else { - parent = schedule.getGroup(bottom, report_step).parent(); - } - - while (parent != top) { - parent = schedule.getGroup(parent, report_step).parent(); - if (parent == top) { - return true; - } else if (parent == "FIELD") { - return false; - } - } - return true; -} - -template -std::string -WellGroupHelpers:: -control_group(const Group& group, - const GroupState& group_state, - const int reportStepIdx, - const Schedule& schedule) -{ - const Group::ProductionCMode& currentGroupControl = group_state.production_control(group.name()); - - if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) { - const auto& parent_name = group.control_group(); - if (parent_name) { - const auto& parent = schedule.getGroup(parent_name.value(), reportStepIdx); - return control_group(parent, - group_state, - reportStepIdx, - schedule); - } - } - - return group.name(); -} - -template -std::pair, Scalar> -WellGroupHelpers:: -worstOffendingWell(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const Group::ProductionCMode& offendedControl, - const Parallel::Communication& comm, - const WellState& wellState, - DeferredLogger& deferred_logger) -{ - std::pair, Scalar> offending_well {std::nullopt, 0.0}; - for (const std::string& child_group : group.groups()) { - const auto& this_group = schedule.getGroup(child_group, reportStepIdx); - const auto & offending_well_this = worstOffendingWell(this_group, - schedule, - reportStepIdx, - offendedControl, - comm, - wellState, - deferred_logger); - if (offending_well_this.second > offending_well.second) { - offending_well = offending_well_this; - } - } - - for (const std::string& child_well : group.wells()) { - - const auto& well_index = wellState.index(child_well); - Scalar violating_rate = 0.0; - Scalar prefered_rate = 0.0; - const auto& pu = wellState.phaseUsageInfo(); - if (well_index.has_value() && wellState.wellIsOwned(well_index.value(), child_well)) - { - const auto& ws = wellState.well(child_well); - switch (offendedControl){ - case Group::ProductionCMode::ORAT: { - const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - violating_rate = ws.surface_rates[oil_pos]; - break; - } - case Group::ProductionCMode::GRAT: { - const int gas_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - violating_rate = ws.surface_rates[gas_pos]; - break; - } - case Group::ProductionCMode::WRAT: { - const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - violating_rate = ws.surface_rates[water_pos]; - break; - } - case Group::ProductionCMode::LRAT: { - assert(pu.phaseIsActive(IndexTraits::oilPhaseIdx)); - assert(pu.phaseIsActive(IndexTraits::waterPhaseIdx)); - const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - violating_rate = ws.surface_rates[oil_pos] + - ws.surface_rates[water_pos]; - break; - } - case Group::ProductionCMode::RESV: - for (int p = 0; p < wellState.numPhases(); ++p) { - violating_rate += ws.reservoir_rates[p]; - } - break; - case Group::ProductionCMode::NONE: - break; - case Group::ProductionCMode::FLD: - break; - case Group::ProductionCMode::PRBL: - OPM_DEFLOG_THROW(std::runtime_error, - "Group " + group.name() + - "GroupProductionCMode PRBL not implemented", deferred_logger); - break; - case Group::ProductionCMode::CRAT: - OPM_DEFLOG_THROW(std::runtime_error, - "Group " + group.name() + - "GroupProductionCMode CRAT not implemented", deferred_logger); - break; - } - const auto preferred_phase = schedule.getWell(child_well, reportStepIdx).getPreferredPhase(); - switch (preferred_phase) { - case Phase::OIL: { - const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - prefered_rate = ws.surface_rates[oil_pos]; - break; - } - case Phase::GAS: { - const int gas_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - prefered_rate = ws.surface_rates[gas_pos]; - break; - } - case Phase::WATER: { - const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - prefered_rate = ws.surface_rates[water_pos]; - break; - } - default: - // No others supported. - break; - } - } - violating_rate = comm.sum(violating_rate); - if (violating_rate < 0 ) { // only check producing wells - prefered_rate = comm.sum(prefered_rate); - Scalar fraction = prefered_rate < -1e-16 ? violating_rate / prefered_rate : 1.0; - if ( fraction > offending_well.second) { - offending_well = {child_well, fraction}; - } - } - } - return offending_well; -} - -template -template -void WellGroupHelpers:: -setRegionAveragePressureCalculator(const Group& group, - const Schedule& schedule, - const int reportStepIdx, - const FieldPropsManager& fp, - std::map>& regionalAveragePressureCalculator) -{ - for (const std::string& groupName : group.groups()) { - setRegionAveragePressureCalculator( schedule.getGroup(groupName, reportStepIdx), schedule, - reportStepIdx, fp, regionalAveragePressureCalculator); - } - const auto& gpm = group.gpmaint(); - if (!gpm) - return; - - const auto& reg = gpm->region(); - if (!reg) - return; - - if (regionalAveragePressureCalculator.count(reg->first) == 0) { - const std::string name = (reg->first.rfind("FIP", 0) == 0) ? reg->first : "FIP" + reg->first; - const auto& fipnum = fp.get_int(name); - regionalAveragePressureCalculator[reg->first] = std::make_unique(fipnum); - } -} - -#include - -template -using AvgP = RegionAverageCalculator:: - AverageRegionalPressure,std::vector>; - -template -using AvgPMap = std::map>>; - -#define INSTANTIATE_TYPE(T) \ -template class WellGroupHelpers; \ -template void WellGroupHelpers:: \ -updateGpMaintTargetForGroups>(const Group&, \ -const Schedule&, \ -const AvgPMap&, \ -int, \ -double, \ -const WellState&, \ -GroupState&); \ -template void WellGroupHelpers:: \ -setRegionAveragePressureCalculator>(const Group&, \ -const Schedule&, \ -const int, \ -const FieldPropsManager&, \ -AvgPMap&); - -INSTANTIATE_TYPE(double) - -#if FLOW_INSTANTIATE_FLOAT -INSTANTIATE_TYPE(float) -#endif - -} // namespace Opm::WellGroupHelpers From a4529e09d0f763c6d5a867e54b6fcef6145815c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Thu, 23 Oct 2025 17:00:21 +0200 Subject: [PATCH 14/26] Made two more update() methods const --- .../wells/BlackoilWellModelConstraints.cpp | 7 +++-- .../wells/BlackoilWellModelConstraints.hpp | 3 ++- .../wells/BlackoilWellModelGeneric.cpp | 6 ++--- .../wells/BlackoilWellModel_impl.hpp | 1 + opm/simulators/wells/WellGroupHelper.cpp | 26 ++++++++++++------- opm/simulators/wells/WellGroupHelper.hpp | 10 +++++-- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index 22923c3437..d63feebf6c 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -494,6 +494,7 @@ updateGroupIndividualControl(const Group& group, std::map>& switched_prod, std::map>& closed_offending_wells, GroupState& group_state, + WellState& well_state, DeferredLogger& deferred_logger) { bool changed = false; @@ -545,7 +546,8 @@ updateGroupIndividualControl(const Group& group, group_state, deferred_logger); wgHelper().updateWellRatesFromGroupTargetScale(changed_this.second, group, - /*is_injector=*/false); + /*is_injector=*/false, + well_state); changed = true; } } @@ -592,7 +594,8 @@ updateGroupIndividualControl(const Group& group, wgHelper().updateWellRatesFromGroupTargetScale(changed_this.second, group, - /*is_injector=*/false); + /*is_injector=*/false, + well_state); } else if (worst_offending_well) { closed_offending_wells.insert_or_assign(group.name(), std::make_pair(Group::ProductionCMode2String(changed_this.first), *worst_offending_well)); diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.hpp b/opm/simulators/wells/BlackoilWellModelConstraints.hpp index 466f923a33..95514dde4c 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.hpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.hpp @@ -73,7 +73,7 @@ public: GroupState& group_state, DeferredLogger& deferred_logger) const; - //! \brief Update the individual controls for wells in a group. Return true if a group control is changed + //! \brief Update the ndividual controls for wells in a group. Return true if a group control is changed bool updateGroupIndividualControl(const Group& group, const int reportStepIdx, const int max_number_of_group_switch, @@ -82,6 +82,7 @@ public: std::map>& switched_prod, std::map>& closed_offending_wells, GroupState& group_state, + WellState& well_state, DeferredLogger& deferred_logger); private: diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 952647c9c0..a87f710871 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -707,7 +707,7 @@ checkGroupHigherConstraints(const Group& group, phase, this->groupState(), deferred_logger); this->wgHelper().updateWellRatesFromGroupTargetScale( - scaling_factor, group, /*is_injector=*/true + scaling_factor, group, /*is_injector=*/true, this->wellState() ); changed = true; } @@ -777,7 +777,7 @@ checkGroupHigherConstraints(const Group& group, switched_prod_groups_[group.name()].push_back(currentControl); } this->wgHelper().updateWellRatesFromGroupTargetScale( - scaling_factor, group, /*is_injector=*/false + scaling_factor, group, /*is_injector=*/false, this->wellState() ); } } @@ -1361,7 +1361,7 @@ updateAndCommunicateGroupData(const int reportStepIdx, wg_helper.updateNetworkLeafNodeProductionRates(this->groupState()); wg_helper.updateGroupProductionRates(fieldGroup, this->groupState()); } - wg_helper.updateWellRates(fieldGroup, this->nupcolWellState()); + wg_helper.updateWellRates(fieldGroup, this->nupcolWellState(), this->wellState()); this->wellState().communicateGroupRates(comm_); this->groupState().communicate_rates(comm_); diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 3551d405be..f9cca02ad9 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -1971,6 +1971,7 @@ namespace Opm { this->switched_prod_groups_, this->closed_offending_wells_, this->groupState(), + this->wellState(), deferred_logger); if (changed_individual) { diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index 4162349ebf..f4d644f2b7 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -1368,17 +1368,21 @@ updateVREPForGroups(const Group& group, GroupState& group_state) const template void WellGroupHelper:: -updateWellRates(const Group& group, const WellState& well_state_nupcol) +updateWellRates( + const Group& group, + const WellState& well_state_nupcol, + WellState& well_state +) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateWellRates(group_tmp, well_state_nupcol); + this->updateWellRates(group_tmp, well_state_nupcol, well_state); } - const int np = this->wellState().numPhases(); + const int np = well_state.numPhases(); for (const std::string& well_name : group.wells()) { std::vector rates(np, 0.0); - const auto well_index = this->wellState().index(well_name); + const auto well_index = well_state.index(well_name); if (well_index.has_value()) { // the well is found on this node const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_); int sign = 1; @@ -1391,13 +1395,15 @@ updateWellRates(const Group& group, const WellState& well_s rates[phase] = sign * ws.surface_rates[phase]; } } - this->wellState().setCurrentWellRates(well_name, rates); + well_state.setCurrentWellRates(well_name, rates); } } template void WellGroupHelper:: -updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool is_injector) +updateWellRatesFromGroupTargetScale( + const Scalar scale, const Group& group, bool is_injector, WellState& well_state +) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1419,11 +1425,11 @@ updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool } if (!individual_control) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); - this->updateWellRatesFromGroupTargetScale(scale, group_tmp, is_injector); + this->updateWellRatesFromGroupTargetScale(scale, group_tmp, is_injector, well_state); } } - const int np = this->wellState().numPhases(); + const int np = well_state.numPhases(); for (const std::string& well_name : group.wells()) { const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_); @@ -1433,11 +1439,11 @@ updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool if (well_tmp.isInjector() && !is_injector) continue; - const auto well_index = this->wellState().index(well_name); + const auto well_index = well_state.index(well_name); if (!well_index.has_value()) continue; - auto& ws = this->wellState().well(well_index.value()); + auto& ws = well_state.well(well_index.value()); if (ws.status == Well::Status::SHUT) continue; diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index aa78e507da..9bfcd003a3 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -208,10 +208,16 @@ public: WellState& well_state, GroupState& group_state ); void updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_state) const; - void updateWellRates(const Group& group, const WellState& well_state_nupcol); + void updateWellRates( + const Group& group, + const WellState& well_state_nupcol, + WellState& well_state + ) const; const WellState& wellState() const { return *this->well_state_; } WellState& wellState() { return *this->well_state_; } - void updateWellRatesFromGroupTargetScale(const Scalar scale, const Group& group, bool is_injector); + void updateWellRatesFromGroupTargetScale( + const Scalar scale, const Group& group, bool is_injector, WellState& well_state + ) const; std::pair, Scalar> worstOffendingWell( const Group& group, const Group::ProductionCMode& offended_control, From cebdf98231246ce75a8107011ef6a57c8a38ebf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 04:37:22 +0200 Subject: [PATCH 15/26] Remove const casts for WellState Remove const casts from wgHelper.wellState() and instead pass non-const WellState as a separate parameter --- .../wells/BlackoilWellModelConstraints.hpp | 5 +- .../wells/BlackoilWellModelNldd_impl.hpp | 2 + .../wells/BlackoilWellModel_impl.hpp | 43 ++++-- opm/simulators/wells/MultisegmentWell.hpp | 4 +- .../wells/MultisegmentWell_impl.hpp | 31 ++-- opm/simulators/wells/StandardWell.hpp | 3 +- opm/simulators/wells/StandardWell_impl.hpp | 30 ++-- opm/simulators/wells/WellGroupConstraints.cpp | 4 +- opm/simulators/wells/WellGroupConstraints.hpp | 4 +- opm/simulators/wells/WellInterface.hpp | 16 ++- .../wells/WellInterfaceFluidSystem.cpp | 9 +- .../wells/WellInterfaceFluidSystem.hpp | 5 +- opm/simulators/wells/WellInterface_impl.hpp | 136 +++++++++++------- 13 files changed, 194 insertions(+), 98 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.hpp b/opm/simulators/wells/BlackoilWellModelConstraints.hpp index 95514dde4c..dd2de86d67 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.hpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.hpp @@ -49,7 +49,7 @@ public: using WellGroupHelperType = WellGroupHelper; //! \brief Constructor initializes reference to the well model. - explicit BlackoilWellModelConstraints(BlackoilWellModelGeneric& wellModel) + explicit BlackoilWellModelConstraints(const BlackoilWellModelGeneric& wellModel) : wellModel_(wellModel) {} @@ -97,9 +97,8 @@ private: checkGroupProductionConstraints(const Group& group, DeferredLogger& deferred_logger) const; - WellGroupHelperType& wgHelper() { return wellModel_.wgHelper(); } const WellGroupHelperType& wgHelper() const { return wellModel_.wgHelper(); } - BlackoilWellModelGeneric& wellModel_; //!< Reference to well model + const BlackoilWellModelGeneric& wellModel_; //!< Reference to well model }; } // namespace Opm diff --git a/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp b/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp index 748004c20e..d4f3ff71d0 100644 --- a/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModelNldd_impl.hpp @@ -64,6 +64,7 @@ assembleWellEq(const double dt, well->assembleWellEq(wellModel_.simulator(), dt, wellModel_.wgHelper(), + wellModel_.wellState(), deferred_logger); } } @@ -192,6 +193,7 @@ updateWellControls(DeferredLogger& deferred_logger, well->updateWellControl(wellModel_.simulator(), mode, wellModel_.wgHelper(), + wellModel_.wellState(), deferred_logger); } } diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index f9cca02ad9..302f9d12ae 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -507,9 +507,11 @@ namespace Opm { try { well->scaleSegmentRatesAndPressure(this->wellState()); well->calculateExplicitQuantities(simulator_, this->wellState(), local_deferredLogger); - well->updateWellStateWithTarget(simulator_, this->wgHelper(), local_deferredLogger); + well->updateWellStateWithTarget(simulator_, this->wgHelper(), this->wellState(), local_deferredLogger); well->updatePrimaryVariables(simulator_, this->wellState(), local_deferredLogger); - well->solveWellEquation(simulator_, this->wgHelper(), local_deferredLogger); + well->solveWellEquation( + simulator_, this->wgHelper(), this->wellState(), local_deferredLogger + ); } catch (const std::exception& e) { const std::string msg = "Compute initial well solution for new well " + well->name() + " failed. Continue with zero initial rates"; local_deferredLogger.warning("WELL_INITIAL_SOLVE_FAILED", msg); @@ -584,6 +586,7 @@ namespace Opm { well->wellTesting(simulator_, simulationTime, this->wgHelper(), + this->wellState(), this->wellTestState(), ecl_well_map, this->well_open_times_, @@ -1394,7 +1397,9 @@ namespace Opm { const Well& well_ecl = this->wells_ecl_[well->indexOfWell()]; const auto inj_controls = Well::InjectionControls(0); const auto prod_controls = well_ecl.productionControls(summary_state); - well->iterateWellEqWithSwitching(this->simulator_, dt, inj_controls, prod_controls, this->wgHelper(), local_deferredLogger, false, false); + well->iterateWellEqWithSwitching( + this->simulator_, dt, inj_controls, prod_controls, this->wgHelper(), this->wellState(), local_deferredLogger, false, false + ); rate = -tcalc.calcModeRateFromRates(ws.surface_rates); group_rate += rate; } @@ -1482,7 +1487,9 @@ namespace Opm { const auto& ws = this->wellState().well(well->indexOfWell()); const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP; if (thp_is_limit) { - well->prepareWellBeforeAssembling(this->simulator_, dt, this->wgHelper(), local_deferredLogger); + well->prepareWellBeforeAssembling( + this->simulator_, dt, this->wgHelper(), this->wellState(), local_deferredLogger + ); } } @@ -1516,7 +1523,9 @@ namespace Opm { { OPM_TIMEFUNCTION(); for (auto& well : well_container_) { - well->prepareWellBeforeAssembling(simulator_, dt, this->wgHelper(), deferred_logger); + well->prepareWellBeforeAssembling( + simulator_, dt, this->wgHelper(), this->wellState(), deferred_logger + ); } } @@ -1798,7 +1807,9 @@ namespace Opm { OPM_BEGIN_PARALLEL_TRY_CATCH() for (const auto& well : well_container_) { const auto mode = WellInterface::IndividualOrGroup::Group; - const bool changed_well = well->updateWellControl(simulator_, mode, this->wgHelper(), deferred_logger); + const bool changed_well = well->updateWellControl( + simulator_, mode, this->wgHelper(), this->wellState(), deferred_logger + ); if (changed_well) { changed_well_to_group = changed_well || changed_well_to_group; } @@ -1821,7 +1832,9 @@ namespace Opm { OPM_BEGIN_PARALLEL_TRY_CATCH() for (const auto& well : well_container_) { const auto mode = WellInterface::IndividualOrGroup::Individual; - const bool changed_well = well->updateWellControl(simulator_, mode, this->wgHelper(), deferred_logger); + const bool changed_well = well->updateWellControl( + simulator_, mode, this->wgHelper(), this->wellState(), deferred_logger + ); if (changed_well) { changed_well_individual = changed_well || changed_well_individual; } @@ -1893,7 +1906,9 @@ namespace Opm { const auto& ws = this->wellState().well(well->indexOfWell()); const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP; if (thp_is_limit) { - well->prepareWellBeforeAssembling(this->simulator_, dt, this->wgHelper(), deferred_logger); + well->prepareWellBeforeAssembling( + this->simulator_, dt, this->wgHelper(), this->wellState(), deferred_logger + ); } } } @@ -1929,7 +1944,9 @@ namespace Opm { if (ws.production_cmode == Well::ProducerCMode::GRUP || ws.injection_cmode == Well::InjectorCMode::GRUP) { - well->updateWellStateWithTarget(simulator_, this->wgHelper(), deferred_logger); + well->updateWellStateWithTarget( + simulator_, this->wgHelper(), this->wellState(), deferred_logger + ); } } OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAndCommunicate failed: ", @@ -2177,7 +2194,9 @@ namespace Opm { for (const auto& well : well_container_) { auto& events = this->wellState().well(well->indexOfWell()).events; if (events.hasEvent(WellState::event_mask)) { - well->updateWellStateWithTarget(simulator_, this->wgHelper(), deferred_logger); + well->updateWellStateWithTarget( + simulator_, this->wgHelper(), this->wellState(), deferred_logger + ); well->updatePrimaryVariables(simulator_, this->wellState(), deferred_logger); // There is no new well control change input within a report step, // so next time step, the well does not consider to have effective events anymore. @@ -2190,7 +2209,9 @@ namespace Opm { // solve the well equation initially to improve the initial solution of the well model if (param_.solve_welleq_initially_ && well->isOperableAndSolvable()) { try { - well->solveWellEquation(simulator_, this->wgHelper(), deferred_logger); + well->solveWellEquation( + simulator_, this->wgHelper(), this->wellState(), deferred_logger + ); } catch (const std::exception& e) { const std::string msg = "Compute initial well solution for " + well->name() + " initially failed. Continue with the previous rates"; deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg); diff --git a/opm/simulators/wells/MultisegmentWell.hpp b/opm/simulators/wells/MultisegmentWell.hpp index 80ff953f16..53a54717ab 100644 --- a/opm/simulators/wells/MultisegmentWell.hpp +++ b/opm/simulators/wells/MultisegmentWell.hpp @@ -92,6 +92,7 @@ namespace Opm { /// updating the well state based the current control mode void updateWellStateWithTarget(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) const override; /// updating the segment pressure and rates based the current bhp and well rates @@ -258,7 +259,6 @@ namespace Opm { DeferredLogger& deferred_logger) const; bool computeWellPotentialsImplicit(const Simulator& simulator, - const WellStateType& well_state, const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const; @@ -270,6 +270,7 @@ namespace Opm { const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) override; bool iterateWellEqWithSwitching(const Simulator& simulator, @@ -277,6 +278,7 @@ namespace Opm { const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false) override; diff --git a/opm/simulators/wells/MultisegmentWell_impl.hpp b/opm/simulators/wells/MultisegmentWell_impl.hpp index 068fe63f6e..3851417cde 100644 --- a/opm/simulators/wells/MultisegmentWell_impl.hpp +++ b/opm/simulators/wells/MultisegmentWell_impl.hpp @@ -183,10 +183,10 @@ namespace Opm MultisegmentWell:: updateWellStateWithTarget(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) const { - auto& well_state = const_cast(wgHelper).wellState(); - Base::updateWellStateWithTarget(simulator, wgHelper, deferred_logger); + Base::updateWellStateWithTarget(simulator, wgHelper, well_state, deferred_logger); // scale segment rates based on the wellRates // and segment pressure based on bhp this->scaleSegmentRatesWithWellRates(this->segments_.inlets(), @@ -310,7 +310,7 @@ namespace Opm debug_cost_counter_ = 0; bool converged_implicit = false; if (this->param_.local_well_solver_control_switching_) { - converged_implicit = computeWellPotentialsImplicit(simulator, well_state, wgHelper, well_potentials, deferred_logger); + converged_implicit = computeWellPotentialsImplicit(simulator, wgHelper, well_potentials, deferred_logger); if (!converged_implicit) { deferred_logger.debug("Implicit potential calculations failed for well " + this->name() + ", reverting to original aproach."); @@ -463,7 +463,7 @@ namespace Opm const double dt = simulator.timeStepSize(); // iterate to get a solution at the given bhp. well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, - deferred_logger); + well_state_copy, deferred_logger); // compute the potential and store in the flux vector. well_flux.clear(); @@ -533,7 +533,6 @@ namespace Opm bool MultisegmentWell:: computeWellPotentialsImplicit(const Simulator& simulator, - const WellStateType& well_state, const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const @@ -545,7 +544,7 @@ namespace Opm well_copy.debug_cost_counter_ = 0; // store a copy of the well state, we don't want to update the real well state - WellStateType well_state_copy = well_state; + WellStateType well_state_copy = wgHelper.wellState(); auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); @@ -584,9 +583,13 @@ namespace Opm // solve equations bool converged = false; if (this->well_ecl_.isProducer()) { - converged = well_copy.solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = well_copy.solveWellWithOperabilityCheck( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + ); } else { - converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = well_copy.iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + ); } // fetch potentials (sign is updated on the outside). @@ -1493,9 +1496,9 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return true; @@ -1618,11 +1621,11 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control /*false*/, const bool fixed_status /*false*/) { - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); const int max_iter_number = this->param_.max_inner_iter_ms_wells_; @@ -1672,10 +1675,10 @@ namespace Opm ++its_since_last_switch; if (allow_switching && its_since_last_switch >= min_its_after_switch && status_switch_count < max_status_switch){ const Scalar wqTotal = this->primary_variables_.getWQTotal().value(); - bool changed = this->updateWellControlAndStatusLocalIteration(simulator, wgHelper, - inj_controls, prod_controls, wqTotal, - deferred_logger, fixed_control, - fixed_status); + bool changed = this->updateWellControlAndStatusLocalIteration( + simulator, wgHelper, inj_controls, prod_controls, wqTotal, + well_state, deferred_logger, fixed_control, fixed_status + ); if (changed) { its_since_last_switch = 0; ++switch_count; diff --git a/opm/simulators/wells/StandardWell.hpp b/opm/simulators/wells/StandardWell.hpp index 827cb05a3e..1bb974661b 100644 --- a/opm/simulators/wells/StandardWell.hpp +++ b/opm/simulators/wells/StandardWell.hpp @@ -200,6 +200,7 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) override; // iterate well equations including control switching @@ -208,6 +209,7 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false) override; @@ -323,7 +325,6 @@ namespace Opm const WellStateType& well_state) const; bool computeWellPotentialsImplicit(const Simulator& ebos_simulator, - const WellStateType& well_state, const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const; diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index 41baca11f3..f587dcef6c 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -1563,7 +1563,9 @@ namespace Opm well_copy.computeAccumWell(); const double dt = simulator.timeStepSize(); - const bool converged = well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + const bool converged = well_copy.iterateWellEqWithControl( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + ); if (!converged) { const std::string msg = " well " + name() + " did not get converged during well potential calculations " " potentials are computed based on unconverged solution"; @@ -1617,7 +1619,6 @@ namespace Opm bool StandardWell:: computeWellPotentialsImplicit(const Simulator& simulator, - const WellStateType& well_state, const WellGroupHelperType& wgHelper, std::vector& well_potentials, DeferredLogger& deferred_logger) const @@ -1628,7 +1629,7 @@ namespace Opm StandardWell well_copy(*this); // store a copy of the well state, we don't want to update the real well state - WellStateType well_state_copy = well_state; + WellStateType well_state_copy = wgHelper.wellState(); auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); @@ -1667,9 +1668,13 @@ namespace Opm // iterate to get a solution at the given bhp. bool converged = false; if (this->well_ecl_.isProducer()) { - converged = well_copy.solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = well_copy.solveWellWithOperabilityCheck( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + ); } else { - converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = well_copy.iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + ); } // fetch potentials (sign is updated on the outside). @@ -1763,7 +1768,9 @@ namespace Opm // group controlled wells with defaulted guiderates will have zero targets as // the potentials are used to compute the well fractions. if (this->param_.local_well_solver_control_switching_ && !(this->changed_to_open_this_step_ && this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger))) { - converged_implicit = computeWellPotentialsImplicit(simulator, well_state, wgHelper, well_potentials, deferred_logger); + converged_implicit = computeWellPotentialsImplicit( + simulator, wgHelper, well_potentials, deferred_logger + ); } if (!converged_implicit) { // does the well have a THP related constraint? @@ -2364,9 +2371,9 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); updatePrimaryVariables(simulator, well_state, deferred_logger); @@ -2412,11 +2419,11 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control /*false*/, const bool fixed_status /*false*/) { - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); updatePrimaryVariables(simulator, well_state, deferred_logger); @@ -2456,9 +2463,10 @@ namespace Opm its_since_last_switch++; if (allow_switching && its_since_last_switch >= min_its_after_switch && status_switch_count < max_status_switch){ const Scalar wqTotal = this->primary_variables_.eval(WQTotal).value(); - changed = this->updateWellControlAndStatusLocalIteration(simulator, wgHelper, - inj_controls, prod_controls, wqTotal, - deferred_logger, fixed_control, fixed_status); + changed = this->updateWellControlAndStatusLocalIteration( + simulator, wgHelper, inj_controls, prod_controls, wqTotal, + well_state, deferred_logger, fixed_control, fixed_status + ); if (changed){ its_since_last_switch = 0; switch_count++; diff --git a/opm/simulators/wells/WellGroupConstraints.cpp b/opm/simulators/wells/WellGroupConstraints.cpp index c9c606760c..bb2e876e8a 100644 --- a/opm/simulators/wells/WellGroupConstraints.cpp +++ b/opm/simulators/wells/WellGroupConstraints.cpp @@ -120,9 +120,9 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate) const + const bool check_guide_rate, + WellStateType& well_state) const { - auto& well_state = const_cast(wgHelper).wellState(); const auto& well = well_.wellEcl(); const int well_index = well_.indexOfWell(); auto& ws = well_state.well(well_index); diff --git a/opm/simulators/wells/WellGroupConstraints.hpp b/opm/simulators/wells/WellGroupConstraints.hpp index f15d16a8b8..c13036d294 100644 --- a/opm/simulators/wells/WellGroupConstraints.hpp +++ b/opm/simulators/wells/WellGroupConstraints.hpp @@ -56,12 +56,14 @@ public: const std::optional&, std::vector&)>; using WellGroupHelperType = WellGroupHelper; + using WellStateType = WellState; bool checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate) const; + const bool check_guide_rate, + WellStateType& well_state) const; private: std::pair diff --git a/opm/simulators/wells/WellInterface.hpp b/opm/simulators/wells/WellInterface.hpp index 428a130893..4d037eaa56 100644 --- a/opm/simulators/wells/WellInterface.hpp +++ b/opm/simulators/wells/WellInterface.hpp @@ -169,6 +169,7 @@ public: void assembleWellEq(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); void assembleWellEqWithoutIteration(const Simulator& simulator, @@ -181,6 +182,7 @@ public: void prepareWellBeforeAssembling(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); virtual void computeWellRatesWithBhp(const Simulator& ebosSimulator, @@ -218,6 +220,7 @@ public: virtual void updateWellStateWithTarget(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) const; virtual void scaleSegmentRatesAndPressure(WellStateType& well_state) const; @@ -250,6 +253,7 @@ public: bool updateWellControl(const Simulator& simulator, const IndividualOrGroup iog, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) /* const */; bool updateWellControlAndStatusLocalIteration(const Simulator& simulator, @@ -257,6 +261,7 @@ public: const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const Scalar WQTotal, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false); @@ -292,6 +297,7 @@ public: void wellTesting(const Simulator& simulator, const double simulation_time, const WellGroupHelperType& wgHelper, + WellStateType& well_state, WellTestState& welltest_state, GLiftEclWells& ecl_well_map, std::map& open_times, @@ -316,7 +322,6 @@ public: DeferredLogger& deferred_logger); bool updateWellOperabilityFromWellEq(const Simulator& simulator, - const WellStateType& well_state, const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger); @@ -339,6 +344,7 @@ public: void solveWellEquation(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); const std::vector& connectionRates() const @@ -362,6 +368,7 @@ public: const WellInjectionControls& inj_controls, const WellProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control = false, const bool fixed_status = false) = 0; @@ -414,6 +421,7 @@ protected: const WellInjectionControls& inj_controls, const WellProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) = 0; virtual void updateIPRImplicit(const Simulator& simulator, @@ -423,6 +431,7 @@ protected: bool iterateWellEquations(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); bool solveWellWithOperabilityCheck(const Simulator& simulator, @@ -430,6 +439,7 @@ protected: const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); std::optional @@ -437,21 +447,25 @@ protected: const double dt, const WellGroupHelperType& wgHelper, const SummaryState& summary_state, + WellStateType& well_state, DeferredLogger& deferred_logger); bool solveWellWithBhp(const Simulator& simulator, const double dt, const Scalar bhp, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); bool solveWellWithZeroRate(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); bool solveWellForTesting(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger); diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index 00e93a10e2..b5ef2d311b 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -182,7 +182,8 @@ WellInterfaceFluidSystem:: checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, - const bool check_guide_rate) const + const bool check_guide_rate, + WellStateType& well_state) const { const auto& group_state = wgHelper.groupState(); @@ -204,7 +205,7 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, return WellGroupConstraints(*this).checkGroupConstraints(wgHelper, schedule, summaryState, - rCoeff, check_guide_rate); + rCoeff, check_guide_rate, well_state); } template @@ -213,16 +214,16 @@ WellInterfaceFluidSystem:: checkConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, + WellStateType& well_state, DeferredLogger& deferred_logger) const { - auto& well_state = const_cast(wgHelper).wellState(); const bool ind_broken = checkIndividualConstraints(well_state.well(this->index_of_well_), summaryState, deferred_logger); if (ind_broken) { return true; } else { return checkGroupConstraints(wgHelper, schedule, - summaryState, true); + summaryState, true, well_state); } } diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.hpp b/opm/simulators/wells/WellInterfaceFluidSystem.hpp index 08605045a0..6550d816c9 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.hpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.hpp @@ -59,6 +59,7 @@ public: using IndexTraits = typename FluidSystem::IndexTraitsType; using ModelParameters = typename WellInterfaceGeneric::ModelParameters; using WellGroupHelperType = WellGroupHelper; + using WellStateType = WellState; static constexpr int Water = IndexTraits::waterPhaseIdx; static constexpr int Oil = IndexTraits::oilPhaseIdx; @@ -93,11 +94,13 @@ protected: bool checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, - const bool check_guide_rate) const; + const bool check_guide_rate, + WellStateType& well_state) const; bool checkConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, + WellStateType& well_state, DeferredLogger& deferred_logger) const; std::optional diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 9958e34ad3..83dfd4d04a 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -189,10 +189,10 @@ namespace Opm updateWellControl(const Simulator& simulator, const IndividualOrGroup iog, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) /* const */ { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); if (stoppedOrZeroRateTarget(simulator, well_state, deferred_logger)) { return false; } @@ -233,10 +233,12 @@ namespace Opm if (iog == IndividualOrGroup::Individual) { changed = this->checkIndividualConstraints(ws, summaryState, deferred_logger); } else if (iog == IndividualOrGroup::Group) { - changed = this->checkGroupConstraints(wgHelper, schedule, summaryState, true); + changed = this->checkGroupConstraints( + wgHelper, schedule, summaryState, true, well_state + ); } else { assert(iog == IndividualOrGroup::Both); - changed = this->checkConstraints(wgHelper, schedule, summaryState, deferred_logger); + changed = this->checkConstraints(wgHelper, schedule, summaryState, well_state, deferred_logger); } Parallel::Communication cc = simulator.vanguard().grid().comm(); // checking whether control changed @@ -262,7 +264,7 @@ namespace Opm if (iterationIdx >= nupcol || this->well_control_log_.empty()) { this->well_control_log_.push_back(from); } - updateWellStateWithTarget(simulator, wgHelper, deferred_logger); + updateWellStateWithTarget(simulator, wgHelper, well_state, deferred_logger); updatePrimaryVariables(simulator, well_state, deferred_logger); } @@ -277,12 +279,12 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const Scalar wqTotal, + WellStateType& well_state, DeferredLogger& deferred_logger, const bool fixed_control, const bool fixed_status) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const auto& summary_state = simulator.vanguard().summaryState(); const auto& schedule = simulator.vanguard().schedule(); auto& ws = well_state.well(this->index_of_well_); @@ -317,7 +319,9 @@ namespace Opm changed = this->checkIndividualConstraints(ws, summary_state, deferred_logger, inj_controls, prod_controls); } if (hasGroupControl && this->param_.check_group_constraints_inner_well_iterations_) { - changed = changed || this->checkGroupConstraints(wgHelper, schedule, summary_state, false); + changed = changed || this->checkGroupConstraints( + wgHelper, schedule, summary_state, false, well_state + ); } if (changed) { @@ -327,7 +331,7 @@ namespace Opm ws.thp = this->getTHPConstraint(summary_state); } else { // don't call for thp since this might trigger additional local solve - updateWellStateWithTarget(simulator, wgHelper, deferred_logger); + updateWellStateWithTarget(simulator, wgHelper, well_state, deferred_logger); } updatePrimaryVariables(simulator, well_state, deferred_logger); } @@ -383,17 +387,18 @@ namespace Opm wellTesting(const Simulator& simulator, const double simulation_time, const WellGroupHelperType& wgHelper, + WellStateType& well_state, WellTestState& well_test_state, GLiftEclWells& ecl_well_map, std::map& open_times, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); deferred_logger.info(" well " + this->name() + " is being tested"); WellStateType well_state_copy = well_state; + auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->indexOfWell()); const auto& summary_state = simulator.vanguard().summaryState(); @@ -431,9 +436,7 @@ namespace Opm // untill the number of closed completions do not increase anymore. while (testWell) { const std::size_t original_number_closed_completions = welltest_state_temp.num_closed_completions(); - // Use RAII guard to temporarily redirect wgHelper to use well_state_copy - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); - bool converged = solveWellForTesting(simulator, wgHelper, deferred_logger); + bool converged = solveWellForTesting(simulator, wgHelper, well_state_copy, deferred_logger); if (!converged) { const auto msg = fmt::format("WTEST: Well {} is not solvable (physical)", this->name()); deferred_logger.debug(msg); @@ -506,10 +509,10 @@ namespace Opm iterateWellEquations(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const auto& summary_state = simulator.vanguard().summaryState(); const auto inj_controls = this->well_ecl_.isInjector() ? this->well_ecl_.injectionControls(summary_state) : Well::InjectionControls(0); const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0); @@ -520,12 +523,16 @@ namespace Opm try { // TODO: the following two functions will be refactored to be one to reduce the code duplication if (!this->param_.local_well_solver_control_switching_){ - converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger); } else { if (this->param_.use_implicit_ipr_ && this->well_ecl_.isProducer() && (well_state.well(this->index_of_well_).status == WellStatus::OPEN)) { - converged = solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = solveWellWithOperabilityCheck( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); } else { - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); } } @@ -571,24 +578,26 @@ namespace Opm const Well::InjectionControls& inj_controls, const Well::ProductionControls& prod_controls, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const auto& summary_state = simulator.vanguard().summaryState(); bool converged = true; auto& ws = well_state.well(this->index_of_well_); // if well is stopped, check if we can reopen if (this->wellIsStopped()) { this->openWell(); - auto bhp_target = estimateOperableBhp(simulator, dt, wgHelper, summary_state, deferred_logger); + auto bhp_target = estimateOperableBhp( + simulator, dt, wgHelper, summary_state, well_state, deferred_logger + ); if (!bhp_target.has_value()) { // no intersection with ipr const auto msg = fmt::format("estimateOperableBhp: Did not find operable BHP for well {}", this->name()); deferred_logger.debug(msg); // well can't operate using explicit fractions stop the well // solve with zero rates - converged = solveWellWithZeroRate(simulator, dt, wgHelper, deferred_logger); + converged = solveWellWithZeroRate(simulator, dt, wgHelper, well_state, deferred_logger); this->stopWell(); this->operability_status_.can_obtain_bhp_with_thp_limit = false; this->operability_status_.obey_thp_limit_under_bhp_limit = false; @@ -598,11 +607,13 @@ namespace Opm ws.thp = this->getTHPConstraint(summary_state); const Scalar bhp = std::max(bhp_target.value(), static_cast(prod_controls.bhp_limit)); - solveWellWithBhp(simulator, dt, bhp, wgHelper, deferred_logger); + solveWellWithBhp(simulator, dt, bhp, wgHelper, well_state, deferred_logger); } } // solve well-equation - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); const bool isThp = ws.production_cmode == Well::ProducerCMode::THP; @@ -622,10 +633,14 @@ namespace Opm if (bhp_stable.has_value() && cur_bhp - bhp_stable.value() > cur_bhp*reltol){ const auto msg = fmt::format("Well {} converged to an unstable solution, re-solving", this->name()); deferred_logger.debug(msg); - solveWellWithBhp(simulator, dt, bhp_stable.value(), wgHelper, deferred_logger); + solveWellWithBhp( + simulator, dt, bhp_stable.value(), wgHelper, well_state, deferred_logger + ); // re-solve with hopefully good initial guess ws.thp = this->getTHPConstraint(summary_state); - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); } } } @@ -634,11 +649,13 @@ namespace Opm // Well did not converge, switch to explicit fractions this->operability_status_.use_vfpexplicit = true; this->openWell(); - auto bhp_target = estimateOperableBhp(simulator, dt, wgHelper, summary_state, deferred_logger); + auto bhp_target = estimateOperableBhp( + simulator, dt, wgHelper, summary_state, well_state, deferred_logger + ); if (!bhp_target.has_value()) { // solve with zero rate // well can't operate using explicit fractions stop the well - converged = solveWellWithZeroRate(simulator, dt, wgHelper, deferred_logger); + converged = solveWellWithZeroRate(simulator, dt, wgHelper, well_state, deferred_logger); this->stopWell(); this->operability_status_.can_obtain_bhp_with_thp_limit = false; this->operability_status_.obey_thp_limit_under_bhp_limit = false; @@ -647,7 +664,9 @@ namespace Opm // solve well with the estimated target bhp (or limit) const Scalar bhp = std::max(bhp_target.value(), static_cast(prod_controls.bhp_limit)); - solveWellWithBhp(simulator, dt, bhp, wgHelper, deferred_logger); + solveWellWithBhp( + simulator, dt, bhp, wgHelper, well_state, deferred_logger + ); ws.thp = this->getTHPConstraint(summary_state); const auto msg = fmt::format("Well {} did not converge, re-solving with explicit fractions for VFP caculations.", this->name()); deferred_logger.debug(msg); @@ -655,6 +674,7 @@ namespace Opm inj_controls, prod_controls, wgHelper, + well_state, deferred_logger); } } @@ -671,12 +691,14 @@ namespace Opm const double dt, const WellGroupHelperType& wgHelper, const SummaryState& summary_state, + WellStateType& well_state, DeferredLogger& deferred_logger) { - auto& well_state = const_cast(wgHelper).wellState(); if (!this->wellHasTHPConstraints(summary_state)) { const Scalar bhp_limit = WellBhpThpCalculator(*this).mostStrictBhpFromBhpLimits(summary_state); - const bool converged = solveWellWithBhp(simulator, dt, bhp_limit, wgHelper, deferred_logger); + const bool converged = solveWellWithBhp( + simulator, dt, bhp_limit, wgHelper, well_state, deferred_logger + ); if (!converged || this->wellIsStopped()) { return std::nullopt; } @@ -688,7 +710,9 @@ namespace Opm // Get minimal bhp from vfp-curve Scalar bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity()); // Solve - const bool converged = solveWellWithBhp(simulator, dt, bhp_min, wgHelper, deferred_logger); + const bool converged = solveWellWithBhp( + simulator, dt, bhp_min, wgHelper, well_state, deferred_logger + ); if (!converged || this->wellIsStopped()) { return std::nullopt; } @@ -705,10 +729,10 @@ namespace Opm const double dt, const Scalar bhp, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); // Solve a well using single bhp-constraint (but close if not operable under this) auto group_state = GroupState(); // empty group auto group_guard = const_cast(wgHelper).pushGroupState(group_state); @@ -732,7 +756,10 @@ namespace Opm // update well-state ws.bhp = bhp; // solve - const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger, /*fixed_control*/true); + const bool converged = this->iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, + well_state, deferred_logger, /*fixed_control*/true + ); ws.injection_cmode = cmode_inj; ws.production_cmode = cmode_prod; return converged; @@ -744,6 +771,7 @@ namespace Opm solveWellWithZeroRate(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); @@ -756,7 +784,10 @@ namespace Opm auto inj_controls = Well::InjectionControls(0); auto prod_controls = Well::ProductionControls(0); - const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger, /*fixed_control*/true, /*fixed_status*/ true); + const bool converged = this->iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, + deferred_logger, /*fixed_control*/true, /*fixed_status*/ true + ); this->wellStatus_ = well_status_orig; return converged; } @@ -766,10 +797,10 @@ namespace Opm WellInterface:: solveWellForTesting(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const double dt = simulator.timeStepSize(); const auto& summary_state = simulator.vanguard().summaryState(); @@ -781,12 +812,18 @@ namespace Opm try { // TODO: the following two functions will be refactored to be one to reduce the code duplication if (!this->param_.local_well_solver_control_switching_){ - converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->iterateWellEqWithControl( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); } else { if (this->param_.use_implicit_ipr_ && this->well_ecl_.isProducer() && (well_state.well(this->index_of_well_).status == WellStatus::OPEN)) { - converged = this->solveWellWithOperabilityCheck(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->solveWellWithOperabilityCheck( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); } else { - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, wgHelper, deferred_logger); + converged = this->iterateWellEqWithSwitching( + simulator, dt, inj_controls, prod_controls, wgHelper, well_state, deferred_logger + ); } } @@ -812,17 +849,17 @@ namespace Opm WellInterface:: solveWellEquation(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return; - auto& well_state = const_cast(wgHelper).wellState(); // keep a copy of the original well state const WellStateType well_state0 = well_state; const double dt = simulator.timeStepSize(); - bool converged = iterateWellEquations(simulator, dt, wgHelper, deferred_logger); + bool converged = iterateWellEquations(simulator, dt, wgHelper, well_state, deferred_logger); // Newly opened wells with THP control sometimes struggles to // converge due to bad initial guess. Or due to the simple fact @@ -855,7 +892,7 @@ namespace Opm const std::string msg = std::string("The newly opened well ") + this->name() + std::string(" with THP control did not converge during inner iterations, we try again with bhp control"); deferred_logger.debug(msg); - converged = this->iterateWellEquations(simulator, dt, wgHelper, deferred_logger); + converged = this->iterateWellEquations(simulator, dt, wgHelper, well_state, deferred_logger); } } @@ -875,12 +912,12 @@ namespace Opm assembleWellEq(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); - prepareWellBeforeAssembling(simulator, dt, wgHelper, deferred_logger); + prepareWellBeforeAssembling(simulator, dt, wgHelper, well_state, deferred_logger); assembleWellEqWithoutIteration(simulator, dt, well_state, group_state, deferred_logger); } @@ -912,10 +949,10 @@ namespace Opm prepareWellBeforeAssembling(const Simulator& simulator, const double dt, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const bool old_well_operable = this->operability_status_.isOperableAndSolvable(); if (this->param_.check_well_operability_iter_) @@ -942,7 +979,9 @@ namespace Opm } this->stopWell(); changed_to_stopped_this_step_ = true; - bool converged_zero_rate = this->solveWellWithZeroRate(simulator, dt, wgHelper, deferred_logger); + bool converged_zero_rate = this->solveWellWithZeroRate( + simulator, dt, wgHelper, well_state, deferred_logger + ); if (this->param_.shut_unsolvable_wells_ && !converged_zero_rate ) { this->operability_status_.solvable = false; } @@ -950,7 +989,9 @@ namespace Opm number_of_well_reopenings_++; return; } - bool converged = this->iterateWellEquations(simulator, dt, wgHelper, deferred_logger); + bool converged = this->iterateWellEquations( + simulator, dt, wgHelper, well_state, deferred_logger + ); if (converged) { const bool zero_target = this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger); @@ -1142,7 +1183,7 @@ namespace Opm { OPM_TIMEFUNCTION(); if (this->param_.local_well_solver_control_switching_) { - const bool success = updateWellOperabilityFromWellEq(simulator, well_state, wgHelper, deferred_logger); + const bool success = updateWellOperabilityFromWellEq(simulator, wgHelper, deferred_logger); if (!success) { this->operability_status_.solvable = false; deferred_logger.debug("Operability check using well equations did not converge for well " @@ -1174,7 +1215,6 @@ namespace Opm bool WellInterface:: updateWellOperabilityFromWellEq(const Simulator& simulator, - const WellStateType& well_state, const WellGroupHelperType& wgHelper, DeferredLogger& deferred_logger) { @@ -1182,11 +1222,11 @@ namespace Opm // only makes sense if we're using this parameter is true assert(this->param_.local_well_solver_control_switching_); this->operability_status_.resetOperability(); - WellStateType well_state_copy = well_state; + WellStateType well_state_copy = wgHelper.wellState(); const double dt = simulator.timeStepSize(); // equations should be converged at this stage, so only one it is needed auto guard = const_cast(wgHelper).pushWellState(well_state_copy); - bool converged = iterateWellEquations(simulator, dt, wgHelper, deferred_logger); + bool converged = iterateWellEquations(simulator, dt, wgHelper, well_state_copy, deferred_logger); return converged; } @@ -1203,10 +1243,10 @@ namespace Opm WellInterface:: updateWellStateWithTarget(const Simulator& simulator, const WellGroupHelperType& wgHelper, + WellStateType& well_state, DeferredLogger& deferred_logger) const { OPM_TIMEFUNCTION(); - auto& well_state = const_cast(wgHelper).wellState(); const auto& group_state = wgHelper.groupState(); // only bhp and wellRates are used to initilize the primaryvariables for standard wells const auto& well = this->well_ecl_; From 425034c71ce0f701169d909a7d4c0a7adec540a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 04:49:14 +0200 Subject: [PATCH 16/26] Make GroupState and WellState pointers const Make the GroupState and WellState pointers in WellGroupHelper const pointers --- opm/simulators/wells/WellGroupHelper.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index 9bfcd003a3..21239c678c 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -67,7 +67,7 @@ public: private: WellGroupHelper& wgHelper_; - WellState* previous_state_ptr_; + const WellState* previous_state_ptr_; }; // RAII guard for temporarily setting groupstate pointer @@ -94,7 +94,7 @@ public: private: WellGroupHelper& wgHelper_; - GroupState* previous_state_ptr_; + const GroupState* previous_state_ptr_; }; WellGroupHelper( @@ -160,7 +160,6 @@ public: const Phase injection_phase ) const; const GroupState& groupState() const { return *this->group_state_; } - GroupState& groupState() { return *this->group_state_; } const PhaseUsageInfo& phaseUsageInfo() const { return this->phase_usage_info_; } GroupStateGuard pushGroupState(GroupState& group_state) { return GroupStateGuard(*this, group_state); @@ -214,7 +213,6 @@ public: WellState& well_state ) const; const WellState& wellState() const { return *this->well_state_; } - WellState& wellState() { return *this->well_state_; } void updateWellRatesFromGroupTargetScale( const Scalar scale, const Group& group, bool is_injector, WellState& well_state ) const; @@ -253,8 +251,8 @@ private: std::vector& group_target_reduction, GroupState& group_state ) const; - WellState* well_state_{nullptr}; - GroupState* group_state_{nullptr}; + const WellState* well_state_{nullptr}; + const GroupState* group_state_{nullptr}; const Schedule& schedule_; const SummaryState& summary_state_; const GuideRate& guide_rate_; From cc5981dfe41af24f19456808693770f06b26a6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 06:26:38 +0200 Subject: [PATCH 17/26] Add vertical space for readability --- opm/simulators/wells/WellGroupHelper.hpp | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index 21239c678c..41e4e30186 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -107,6 +107,7 @@ public: ); void accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) const; + std::pair checkGroupConstraintsInj( const std::string& name, const std::string& parent, @@ -117,6 +118,7 @@ public: const std::vector& resv_coeff, const bool check_guide_rate ) const; + std::pair checkGroupConstraintsProd( const std::string& name, const std::string& parent, @@ -126,14 +128,19 @@ public: const std::vector& resv_coeff, const bool check_guide_rate ) const; + std::map computeNetworkPressures( const Network::ExtNetwork& network, const VFPProdProperties& vfp_prod_props, const Parallel::Communication& comm ) const; + DeferredLogger& deferredLogger() const { return *this->deferred_logger_; } + Scalar getGuideRate(const std::string& name, const GuideRateModel::Target target) const; + GuideRate::RateVector getProductionGroupRateVector(const std::string& group_name) const; + Scalar getWellGroupTargetInjector( const std::string& name, const std::string& parent, @@ -143,6 +150,7 @@ public: const Scalar efficiency_factor, const std::vector& resv_coeff ) const; + Scalar getWellGroupTargetProducer( const std::string& name, const std::string& parent, @@ -151,34 +159,49 @@ public: const Scalar efficiency_factor, const std::vector& resv_coeff ) const; + GuideRate::RateVector getWellRateVector(const std::string& name) const; + std::vector groupChainTopBot(const std::string& bottom, const std::string& top) const; + int groupControlledWells( const std::string& group_name, const std::string& always_included_child, const bool is_production_group, const Phase injection_phase ) const; + const GroupState& groupState() const { return *this->group_state_; } + const PhaseUsageInfo& phaseUsageInfo() const { return this->phase_usage_info_; } + GroupStateGuard pushGroupState(GroupState& group_state) { return GroupStateGuard(*this, group_state); } + WellStateGuard pushWellState(WellState& well_state) { return WellStateGuard(*this, well_state); } + void setCmodeGroup(const Group& group, GroupState& group_state) const; + void setLogger(DeferredLogger* deferred_logger) { deferred_logger_ = deferred_logger; } + template void setRegionAveragePressureCalculator( const Group& group, const FieldPropsManager& fp, std::map>& regional_average_pressure_calculator ) const; + void setReportStep(int report_step) { report_step_ = report_step; } + Scalar sumSolventRates(const Group& group, const bool is_injector) const; + Scalar sumWellResRates(const Group& group, const int phase_pos, const bool injector) const; + Scalar sumWellSurfaceRates(const Group& group, const int phase_pos, const bool injector) const; + Scalar sumWellPhaseRates( bool res_rates, const Group& group, @@ -186,36 +209,50 @@ public: const bool injector, const bool network = false ) const; + template void updateGpMaintTargetForGroups( const Group& group, const RegionalValues& regional_values, const double dt, GroupState& group_state ) const; + int updateGroupControlledWells( const bool is_production_group, const Phase injection_phase, GroupState& group_state ) const; + void updateGroupProductionRates(const Group& group, GroupState& group_state) const; + void updateGroupTargetReduction( const Group& group, const bool is_injector, GroupState& group_state ) const; + void updateNetworkLeafNodeProductionRates(GroupState& group_state) const; + void updateREINForGroups(const Group& group, bool sum_rank, GroupState& group_state) const; + void updateReservoirRatesInjectionGroups(const Group& group, GroupState& group_state) const; + void updateVREPForGroups(const Group& group, GroupState& group_state) const; + void updateState( WellState& well_state, GroupState& group_state ); + void updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_state) const; + void updateWellRates( const Group& group, const WellState& well_state_nupcol, WellState& well_state ) const; + const WellState& wellState() const { return *this->well_state_; } + void updateWellRatesFromGroupTargetScale( const Scalar scale, const Group& group, bool is_injector, WellState& well_state ) const; + std::pair, Scalar> worstOffendingWell( const Group& group, const Group::ProductionCMode& offended_control, @@ -223,34 +260,43 @@ public: ) const; private: std::string controlGroup_(const Group& group) const; + GuideRate::RateVector getGuideRateVector_(const std::vector& rates) const; + bool isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const; + int phaseToActivePhaseIdx_(const Phase phase) const; + Scalar satelliteInjectionRate_( const ScheduleState& sched, const Group& group, const int phase_pos, bool res_rates ) const; + Scalar satelliteProductionRate_( const ScheduleState& sched, const Group& group, const GSatProd::GSatProdGroupProp::Rate rate_comp, bool res_rates ) const; + std::optional selectRateComponent_(const int phase_pos) const; + int updateGroupControlledWellsRecursive_( const std::string& group_name, const bool is_production_group, const Phase injection_phase, GroupState& group_state ) const; + void updateGroupTargetReductionRecursive_( const Group& group, const bool is_injector, std::vector& group_target_reduction, GroupState& group_state ) const; + const WellState* well_state_{nullptr}; const GroupState* group_state_{nullptr}; const Schedule& schedule_; From 0108c6af6936d9294c67b346dd7e1c482a97a125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 06:30:39 +0200 Subject: [PATCH 18/26] Reintroduce original comments Forgot to copy along the function comments in the header when I refactored the WellGroupHelpers.hpp file. --- opm/simulators/wells/WellGroupHelper.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index 41e4e30186..d57f6a9bfb 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -164,6 +164,8 @@ public: std::vector groupChainTopBot(const std::string& bottom, const std::string& top) const; + /// returns the number of wells that are actively under group control for a given group with name given + /// by group_name int groupControlledWells( const std::string& group_name, const std::string& always_included_child, @@ -217,6 +219,8 @@ public: GroupState& group_state ) const; + /// update the number of wells that are actively under group control for a given group with name given by group_name + /// its main usage is to detect cases where there is no wells under group control int updateGroupControlledWells( const bool is_production_group, const Phase injection_phase, GroupState& group_state ) const; @@ -253,6 +257,7 @@ public: const Scalar scale, const Group& group, bool is_injector, WellState& well_state ) const; + /// Returns the name of the worst offending well and its fraction (i.e. violated_phase / preferred_phase) std::pair, Scalar> worstOffendingWell( const Group& group, const Group::ProductionCMode& offended_control, @@ -263,6 +268,7 @@ private: GuideRate::RateVector getGuideRateVector_(const std::vector& rates) const; + /// check if well/group bottom is a sub well/group of the group top bool isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const; int phaseToActivePhaseIdx_(const Phase phase) const; From bf80a5a1ba56d044f5f59715654020c08288bf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 07:53:47 +0200 Subject: [PATCH 19/26] Used clang-format on new files Used clang-format with ColumLimit=110 on the new files --- opm/simulators/wells/WellGroupHelper.cpp | 1104 ++++++++++------------ opm/simulators/wells/WellGroupHelper.hpp | 452 +++++---- 2 files changed, 744 insertions(+), 812 deletions(-) diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index f4d644f2b7..e789dc5bdf 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -30,23 +30,24 @@ #include -namespace Opm { +namespace Opm +{ -template -WellGroupHelper::WellGroupHelper( - WellState& well_state, - GroupState& group_state, - const Schedule& schedule, - const SummaryState& summary_state, - const GuideRate& guide_rate, - const PhaseUsageInfo& phase_usage_info +template +WellGroupHelper::WellGroupHelper(WellState& well_state, + GroupState& group_state, + const Schedule& schedule, + const SummaryState& summary_state, + const GuideRate& guide_rate, + const PhaseUsageInfo& phase_usage_info -) : well_state_{&well_state} - , group_state_{&group_state} - , schedule_{schedule} - , summary_state_{summary_state} - , guide_rate_{guide_rate} - , phase_usage_info_{phase_usage_info} + ) + : well_state_ {&well_state} + , group_state_ {&group_state} + , schedule_ {schedule} + , summary_state_ {summary_state} + , guide_rate_ {guide_rate} + , phase_usage_info_ {phase_usage_info} { } @@ -55,27 +56,27 @@ WellGroupHelper::WellGroupHelper( // Public methods // --------------------------------------------------------------------- -template -void WellGroupHelper:: -accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) const +template +void +WellGroupHelper::accumulateGroupEfficiencyFactor(const Group& group, + Scalar& factor) const { factor *= group.getGroupEfficiencyFactor(); if (group.parent() != "FIELD" && !group.parent().empty()) - this->accumulateGroupEfficiencyFactor( - this->schedule_.getGroup(group.parent(), this->report_step_), factor); + this->accumulateGroupEfficiencyFactor(this->schedule_.getGroup(group.parent(), this->report_step_), + factor); } -template +template std::pair -WellGroupHelper:: -checkGroupConstraintsInj(const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Phase injection_phase, - const Scalar efficiency_factor, - const std::vector& resv_coeff, - const bool check_guide_rate) const +WellGroupHelper::checkGroupConstraintsInj(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate) const { // When called for a well ('name' is a well name), 'parent' // will be the name of 'group'. But if we recurse, 'name' and @@ -90,25 +91,22 @@ checkGroupConstraintsInj(const std::string& name, auto current_group_control = this->groupState().injection_control(group.name(), injection_phase); - if (current_group_control == Group::InjectionCMode::FLD || - current_group_control == Group::InjectionCMode::NONE) - { + if (current_group_control == Group::InjectionCMode::FLD + || current_group_control == Group::InjectionCMode::NONE) { // Return if we are not available for parent group. if (!group.injectionGroupControlAvailable(injection_phase)) { return std::make_pair(false, Scalar(1)); } // Otherwise: check production share of parent's control. const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); - return this->checkGroupConstraintsInj( - name, - parent, - parent_group, - rates, - injection_phase, - efficiency_factor * group.getGroupEfficiencyFactor(), - resv_coeff, - check_guide_rate - ); + return this->checkGroupConstraintsInj(name, + parent, + parent_group, + rates, + injection_phase, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff, + check_guide_rate); } // This can be false for FLD-controlled groups, we must therefore @@ -121,10 +119,11 @@ checkGroupConstraintsInj(const std::string& name, // This is the group containing the control we will check against. Scalar sales_target = 0; if (this->schedule_[this->report_step_].gconsale().has(group.name())) { - const auto& gconsale = this->schedule_[this->report_step_].gconsale().get(group.name(), this->summary_state_); + const auto& gconsale + = this->schedule_[this->report_step_].gconsale().get(group.name(), this->summary_state_); sales_target = gconsale.sales_target; } - WGHelpers::InjectionTargetCalculator tcalc{ + WGHelpers::InjectionTargetCalculator tcalc { current_group_control, this->phase_usage_info_, resv_coeff, @@ -133,33 +132,28 @@ checkGroupConstraintsInj(const std::string& name, this->groupState(), injection_phase, group.has_gpmaint_control(injection_phase, current_group_control), - this->deferredLogger() - }; + this->deferredLogger()}; - WGHelpers::FractionCalculator fcalc{ - this->schedule_, - *this, - this->summary_state_, - this->report_step_, - &this->guide_rate_, - tcalc.guideTargetMode(), - /*is_production_group=*/false, - injection_phase - }; + WGHelpers::FractionCalculator fcalc {this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + /*is_production_group=*/false, + injection_phase}; auto local_fraction_lambda = [&](const std::string& child) { return fcalc.localFraction(child, name); }; - auto local_reduction_lambda = [&](const std::string& group_name) - { - const std::vector& group_target_reductions = - this->groupState().injection_reduction_rates(group_name); + auto local_reduction_lambda = [&](const std::string& group_name) { + const std::vector& group_target_reductions + = this->groupState().injection_reduction_rates(group_name); return tcalc.calcModeRateFromRates(group_target_reductions); }; - auto local_current_rate_lambda = [&](const std::string& group_name) - { - const std::vector& group_surface_rates = - this->groupState().injection_surface_rates(group_name); + auto local_current_rate_lambda = [&](const std::string& group_name) { + const std::vector& group_surface_rates + = this->groupState().injection_surface_rates(group_name); return tcalc.calcModeRateFromRates(group_surface_rates); }; @@ -169,12 +163,10 @@ checkGroupConstraintsInj(const std::string& name, // we need to find out the level where the current well is applied to the local reduction std::size_t local_reduction_level = 0; for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = this->groupControlledWells( - chain[ii], - /*always_included_child=*/"", - /*is_production_group=*/false, - injection_phase - ); + const int num_gr_ctrl = this->groupControlledWells(chain[ii], + /*always_included_child=*/"", + /*is_production_group=*/false, + injection_phase); if (this->guide_rate_.has(chain[ii], injection_phase) && num_gr_ctrl > 0) { local_reduction_level = ii; } @@ -185,10 +177,10 @@ checkGroupConstraintsInj(const std::string& name, for (std::size_t ii = 1; ii < num_ancestors; ++ii) { if (this->guide_rate_.has(chain[ii], injection_phase)) { const auto& guided_group = chain[ii]; - const Scalar grefficiency = - this->schedule_.getGroup(guided_group, this->report_step_).getGroupEfficiencyFactor(); - const Scalar current_rate_fraction = grefficiency * local_current_rate_lambda(guided_group) / - local_current_rate_lambda(chain[ii-1]); + const Scalar grefficiency + = this->schedule_.getGroup(guided_group, this->report_step_).getGroupEfficiencyFactor(); + const Scalar current_rate_fraction = grefficiency * local_current_rate_lambda(guided_group) + / local_current_rate_lambda(chain[ii - 1]); const Scalar guiderate_fraction = local_fraction_lambda(guided_group); // we add a factor here to avoid switching due to numerical instability const Scalar factor = 1.01; @@ -199,10 +191,12 @@ checkGroupConstraintsInj(const std::string& name, } } - if (this->schedule_.hasWell(name) && this->wellState().well(name).group_target) { // for wells we already have computed the target + if (this->schedule_.hasWell(name) + && this->wellState().well(name).group_target) { // for wells we already have computed the target Scalar scale = 1.0; const Scalar group_target_rate_available = *this->wellState().well(name).group_target; - const Scalar current_well_rate_available = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. + const Scalar current_well_rate_available + = tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers. if (current_well_rate_available > 1e-12) { scale = group_target_rate_available / current_well_rate_available; } @@ -228,12 +222,12 @@ checkGroupConstraintsInj(const std::string& name, // Apply local reductions only at the control level // (top) and for levels where we have a specified // group guide rate. - if (local_reduction_level >= ii ) { + if (local_reduction_level >= ii) { target -= local_reduction_lambda(chain[ii]); } // Add my reduction back at the level where it is included in the local reduction - if (local_reduction_level == ii ) { + if (local_reduction_level == ii) { target += current_rate_available * efficiency_factor; } } @@ -248,16 +242,15 @@ checkGroupConstraintsInj(const std::string& name, return std::make_pair(current_rate_available > target_rate_available, scale); } -template +template std::pair -WellGroupHelper:: -checkGroupConstraintsProd(const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Scalar efficiency_factor, - const std::vector& resv_coeff, - const bool check_guide_rate) const +WellGroupHelper::checkGroupConstraintsProd(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate) const { // When called for a well ('name' is a well name), 'parent' // will be the name of 'group'. But if we recurse, 'name' and @@ -272,7 +265,8 @@ checkGroupConstraintsProd(const std::string& name, OPM_TIMEFUNCTION(); const Group::ProductionCMode& current_group_control = this->groupState().production_control(group.name()); - if (current_group_control == Group::ProductionCMode::FLD || current_group_control == Group::ProductionCMode::NONE) { + if (current_group_control == Group::ProductionCMode::FLD + || current_group_control == Group::ProductionCMode::NONE) { // Return if we are not available for parent group. if (!group.productionGroupControlAvailable()) { return std::make_pair(false, 1); @@ -303,40 +297,33 @@ checkGroupConstraintsProd(const std::string& name, if (this->groupState().has_grat_sales_target(group.name())) grat_target_from_sales = this->groupState().grat_sales_target(group.name()); - WGHelpers::TargetCalculator tcalc{ - current_group_control, - this->phase_usage_info_, - resv_coeff, - grat_target_from_sales, - group.name(), - this->groupState(), - group.has_gpmaint_control(current_group_control) - }; + WGHelpers::TargetCalculator tcalc {current_group_control, + this->phase_usage_info_, + resv_coeff, + grat_target_from_sales, + group.name(), + this->groupState(), + group.has_gpmaint_control(current_group_control)}; - WGHelpers::FractionCalculator fcalc{ - this->schedule_, - *this, - this->summary_state_, - this->report_step_, - &this->guide_rate_, - tcalc.guideTargetMode(), - /*is_production_group=*/true, - /*injection_phase=*/Phase::OIL - }; + WGHelpers::FractionCalculator fcalc {this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + /*is_production_group=*/true, + /*injection_phase=*/Phase::OIL}; auto local_fraction_lambda = [&](const std::string& child) { return fcalc.localFraction(child, name); }; - auto local_reduction_lambda = [&](const std::string& group_name) - { - const std::vector& group_target_reductions = - this->groupState().production_reduction_rates(group_name); + auto local_reduction_lambda = [&](const std::string& group_name) { + const std::vector& group_target_reductions + = this->groupState().production_reduction_rates(group_name); return tcalc.calcModeRateFromRates(group_target_reductions); }; - auto local_current_rate_lambda = [&](const std::string& group_name) - { - const std::vector& group_surface_rates = - this->groupState().production_rates(group_name); + auto local_current_rate_lambda = [&](const std::string& group_name) { + const std::vector& group_surface_rates = this->groupState().production_rates(group_name); return tcalc.calcModeRateFromRates(group_surface_rates); }; @@ -359,11 +346,10 @@ checkGroupConstraintsProd(const std::string& name, for (std::size_t ii = 1; ii < num_ancestors; ++ii) { if (this->guide_rate_.has(chain[ii])) { const auto& guided_group = chain[ii]; - const Scalar grefficiency = - this->schedule_.getGroup(guided_group, this->report_step_).getGroupEfficiencyFactor(); - const Scalar current_rate_fraction = - grefficiency * local_current_rate_lambda(guided_group) / - (local_current_rate_lambda(chain[ii-1])); + const Scalar grefficiency + = this->schedule_.getGroup(guided_group, this->report_step_).getGroupEfficiencyFactor(); + const Scalar current_rate_fraction = grefficiency * local_current_rate_lambda(guided_group) + / (local_current_rate_lambda(chain[ii - 1])); const Scalar guiderate_fraction = local_fraction_lambda(guided_group); // we add a factor here to avoid switching due to numerical instability const Scalar factor = 1.01; @@ -391,12 +377,10 @@ checkGroupConstraintsProd(const std::string& name, // we need to find out the level where the current well is applied to the local reduction std::size_t local_reduction_level = 0; for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = this->groupControlledWells( - chain[ii], - /*always_included_child=*/"", - /*is_producer=*/true, - /*injection_phase_not_used=*/Phase::OIL - ); + const int num_gr_ctrl = this->groupControlledWells(chain[ii], + /*always_included_child=*/"", + /*is_producer=*/true, + /*injection_phase_not_used=*/Phase::OIL); if (this->guide_rate_.has(chain[ii]) && num_gr_ctrl > 0) { local_reduction_level = ii; } @@ -409,11 +393,11 @@ checkGroupConstraintsProd(const std::string& name, // Apply local reductions only at the control level // (top) and for levels where we have a specified // group guide rate. - if (local_reduction_level >= ii ) { + if (local_reduction_level >= ii) { target -= local_reduction_lambda(chain[ii]); } // Add my reduction back at the level where it is included in the local reduction - if (local_reduction_level == ii ) { + if (local_reduction_level == ii) { target += current_rate_available * efficiency_factor; } } @@ -429,12 +413,11 @@ checkGroupConstraintsProd(const std::string& name, return std::make_pair(current_rate_available > target_rate_available, scale); } -template +template std::map -WellGroupHelper:: -computeNetworkPressures(const Network::ExtNetwork& network, - const VFPProdProperties& vfp_prod_props, - const Parallel::Communication& comm) const +WellGroupHelper::computeNetworkPressures(const Network::ExtNetwork& network, + const VFPProdProperties& vfp_prod_props, + const Parallel::Communication& comm) const { // TODO: Only dealing with production networks for now. OPM_TIMEFUNCTION(); @@ -453,7 +436,7 @@ computeNetworkPressures(const Network::ExtNetwork& network, std::stack children; std::set leaf_nodes; std::vector root_to_child_nodes; - //children.push(network.root().name()); + // children.push(network.root().name()); children.push(root.get().name()); while (!children.empty()) { const auto node = children.top(); @@ -487,16 +470,18 @@ computeNetworkPressures(const Network::ExtNetwork& network, Scalar alq = 0.0; for (const std::string& wellname : group.wells()) { const Well& well = this->schedule_.getWell(wellname, this->report_step_); - if (well.isInjector() || !this->wellState().isOpen(wellname)) continue; - const Scalar efficiency = well.getEfficiencyFactor(/*network*/ true) * - this->wellState().getGlobalEfficiencyScalingFactor(wellname); + if (well.isInjector() || !this->wellState().isOpen(wellname)) + continue; + const Scalar efficiency = well.getEfficiencyFactor(/*network*/ true) + * this->wellState().getGlobalEfficiencyScalingFactor(wellname); const auto& well_index = this->wellState().index(wellname); - if (well_index.has_value() && this->wellState().wellIsOwned(well_index.value(), wellname)) { + if (well_index.has_value() + && this->wellState().wellIsOwned(well_index.value(), wellname)) { alq += this->wellState().well(wellname).alq_state.get() * efficiency; } } alq = comm.sum(alq); - node_inflows[node][IndexTraits::gasPhaseIdx] += alq; + node_inflows[node][IndexTraits::gasPhaseIdx] += alq; } } @@ -517,16 +502,16 @@ computeNetworkPressures(const Network::ExtNetwork& network, if (up.empty()) { up = std::vector(down.size(), 0.0); } - assert (up.size() == down.size()); + assert(up.size() == down.size()); for (std::size_t ii = 0; ii < up.size(); ++ii) { - up[ii] += efficiency*down[ii]; + up[ii] += efficiency * down[ii]; } } } // Going the other way (from roots to leafs), calculate the pressure // at each node using VFP tables and rates. - //std::map node_pressures; + // std::map node_pressures; for (const auto& node : root_to_child_nodes) { auto press = network.node(node).terminal_pressure(); if (press) { @@ -542,8 +527,8 @@ computeNetworkPressures(const Network::ExtNetwork& network, // convention that production rates are negative, so we must // take a copy and flip signs. auto rates = node_inflows[node]; - std::transform(rates.begin(), rates.end(), rates.begin(), - [](const auto r) { return -r; }); + std::transform( + rates.begin(), rates.end(), rates.begin(), [](const auto r) { return -r; }); assert(rates.size() == 3); // NB! ALQ in extended network is never implicitly the gas lift rate (GRAT), i.e., the // gas lift rates only enters the network pressure calculations through the rates @@ -551,30 +536,32 @@ computeNetworkPressures(const Network::ExtNetwork& network, // // @TODO: Standard network Scalar alq = (*upbranch).alq_value().value_or(0.0); - node_pressures[node] = vfp_prod_props.bhp(*vfp_table, - rates[IndexTraits::waterPhaseIdx], - rates[IndexTraits::oilPhaseIdx], - rates[IndexTraits::gasPhaseIdx], - up_press, - alq, - 0.0, //explicit_wfr - 0.0, //explicit_gfr - false); //use_expvfp we dont support explicit lookup + node_pressures[node] + = vfp_prod_props.bhp(*vfp_table, + rates[IndexTraits::waterPhaseIdx], + rates[IndexTraits::oilPhaseIdx], + rates[IndexTraits::gasPhaseIdx], + up_press, + alq, + 0.0, // explicit_wfr + 0.0, // explicit_gfr + false); // use_expvfp we dont support explicit lookup #define EXTRA_DEBUG_NETWORK 0 #if EXTRA_DEBUG_NETWORK std::ostringstream oss; - oss << "parent: " << (*upbranch).uptree_node() << " child: " << node - << " rates = [ " << rates[0]*86400 << ", " << rates[1]*86400 << ", " << rates[2]*86400 << " ]" - << " p(parent) = " << up_press/1e5 << " p(child) = " << node_pressures[node]/1e5 << std::endl; + oss << "parent: " << (*upbranch).uptree_node() << " child: " << node << " rates = [ " + << rates[0] * 86400 << ", " << rates[1] * 86400 << ", " << rates[2] * 86400 << " ]" + << " p(parent) = " << up_press / 1e5 << " p(child) = " << node_pressures[node] / 1e5 + << std::endl; OpmLog::debug(oss.str()); #endif } else { // Table number specified as 9999 in the deck, no pressure loss. - if (network.node(node).as_choke()){ + if (network.node(node).as_choke()) { // Node pressure is set to the group THP. node_pressures[node] = this->groupState().well_group_thp(node); } else { - node_pressures[node] = up_press; + node_pressures[node] = up_press; } } } @@ -583,10 +570,10 @@ computeNetworkPressures(const Network::ExtNetwork& network, return node_pressures; } -template +template Scalar -WellGroupHelper:: -getGuideRate(const std::string& name, const GuideRateModel::Target target) const +WellGroupHelper::getGuideRate(const std::string& name, + const GuideRateModel::Target target) const { if (this->schedule_.hasWell(name, this->report_step_)) { if (this->guide_rate_.has(name) || this->guide_rate_.hasPotentials(name)) { @@ -605,7 +592,7 @@ getGuideRate(const std::string& name, const GuideRateModel::Target target) const for (const std::string& group_name : group.groups()) { const Group::ProductionCMode& current_group_control - = this->groupState().production_control(group_name); + = this->groupState().production_control(group_name); if (current_group_control == Group::ProductionCMode::FLD || current_group_control == Group::ProductionCMode::NONE) { // accumulate from sub wells/groups @@ -623,7 +610,7 @@ getGuideRate(const std::string& name, const GuideRateModel::Target target) const if (!well_index.has_value()) continue; - if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + if (!this->wellState().wellIsOwned(well_index.value(), well_name)) // Only sum once { continue; } @@ -632,7 +619,7 @@ getGuideRate(const std::string& name, const GuideRateModel::Target target) const if (ws.status == Well::Status::SHUT) continue; - if (! this->wellState().isProductionGrup(well_name)) + if (!this->wellState().isProductionGrup(well_name)) continue; // Only count wells under group control or the ru @@ -640,29 +627,26 @@ getGuideRate(const std::string& name, const GuideRateModel::Target target) const continue; total_guide_rate += this->getGuideRate(well_name, target); - } return total_guide_rate; } -template +template GuideRate::RateVector -WellGroupHelper:: -getProductionGroupRateVector(const std::string& group_name) const +WellGroupHelper::getProductionGroupRateVector(const std::string& group_name) const { return this->getGuideRateVector_(this->groupState().production_rates(group_name)); } -template +template Scalar -WellGroupHelper:: -getWellGroupTargetInjector(const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - Phase injection_phase, - const Scalar efficiency_factor, - const std::vector& resv_coeff) const +WellGroupHelper::getWellGroupTargetInjector(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff) const { // This function computes a wells group target. // 'parent' will be the name of 'group'. But if we recurse, 'name' and @@ -672,7 +656,8 @@ getWellGroupTargetInjector(const std::string& name, // part of. Later it is the accumulated factor including the group efficiency factor // of the child of group. auto current_group_control = this->groupState().injection_control(group.name(), injection_phase); - if (current_group_control == Group::InjectionCMode::FLD || current_group_control == Group::InjectionCMode::NONE) { + if (current_group_control == Group::InjectionCMode::FLD + || current_group_control == Group::InjectionCMode::NONE) { // Return if we are not available for parent group. if (!group.injectionGroupControlAvailable(injection_phase)) { return std::numeric_limits::max(); @@ -680,12 +665,12 @@ getWellGroupTargetInjector(const std::string& name, // Otherwise: check production share of parent's control. const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); return this->getWellGroupTargetInjector(name, - parent, - parent_group, - rates, - injection_phase, - efficiency_factor * group.getGroupEfficiencyFactor(), - resv_coeff); + parent, + parent_group, + rates, + injection_phase, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff); } // This can be false for FLD-controlled groups, we must therefore @@ -698,11 +683,11 @@ getWellGroupTargetInjector(const std::string& name, // This is the group containing the control we will check against. Scalar sales_target = 0; if (this->schedule_[this->report_step_].gconsale().has(group.name())) { - const auto& gconsale = - this->schedule_[this->report_step_].gconsale().get(group.name(), this->summary_state_); + const auto& gconsale + = this->schedule_[this->report_step_].gconsale().get(group.name(), this->summary_state_); sales_target = gconsale.sales_target; } - WGHelpers::InjectionTargetCalculator tcalc{ + WGHelpers::InjectionTargetCalculator tcalc { current_group_control, this->phase_usage_info_, resv_coeff, @@ -711,28 +696,24 @@ getWellGroupTargetInjector(const std::string& name, this->groupState(), injection_phase, group.has_gpmaint_control(injection_phase, current_group_control), - this->deferredLogger() - }; + this->deferredLogger()}; - WGHelpers::FractionCalculator fcalc{ - this->schedule_, - *this, - this->summary_state_, - this->report_step_, - &this->guide_rate_, - tcalc.guideTargetMode(), - /*is_production_group=*/false, - injection_phase - }; + WGHelpers::FractionCalculator fcalc {this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + /*is_production_group=*/false, + injection_phase}; auto local_fraction_lambda = [&](const std::string& child, const std::string& always_incluced_name) { return fcalc.localFraction(child, always_incluced_name); }; - auto local_reduction_lambda = [&](const std::string& group_name) - { - const std::vector& group_target_reductions = - this->groupState().injection_reduction_rates(group_name); + auto local_reduction_lambda = [&](const std::string& group_name) { + const std::vector& group_target_reductions + = this->groupState().injection_reduction_rates(group_name); return tcalc.calcModeRateFromRates(group_target_reductions); }; @@ -753,12 +734,10 @@ getWellGroupTargetInjector(const std::string& name, // we need to find out the level where the local reduction is applied std::size_t local_reduction_level = 0; for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = this->groupControlledWells( - chain[ii], - /*always_included_child=*/"", - /*is_production_group=*/false, - injection_phase - ); + const int num_gr_ctrl = this->groupControlledWells(chain[ii], + /*always_included_child=*/"", + /*is_production_group=*/false, + injection_phase); if (this->guide_rate_.has(chain[ii], injection_phase) && num_gr_ctrl > 0) { local_reduction_level = ii; } @@ -771,11 +750,12 @@ getWellGroupTargetInjector(const std::string& name, // Apply local reductions only at the control level // (top) and for levels where we have a specified // group guide rate. - if (local_reduction_level >= ii ) { + if (local_reduction_level >= ii) { target -= local_reduction_lambda(chain[ii]); } - // If we are under individual control we need to add the wells rate back at the level where it is included in the local reduction + // If we are under individual control we need to add the wells rate back at the level where it is + // included in the local reduction if (local_reduction_level == ii && !this->wellState().isInjectionGrup(name)) { target += current_rate_available * efficiency_factor; } @@ -790,15 +770,14 @@ getWellGroupTargetInjector(const std::string& name, return std::max(Scalar(0.0), target / efficiency_factor); } -template +template Scalar -WellGroupHelper:: -getWellGroupTargetProducer(const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Scalar efficiency_factor, - const std::vector& resv_coeff) const +WellGroupHelper::getWellGroupTargetProducer(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff) const { // This function computes a wells group target. // 'parent' will be the name of 'group'. But if we recurse, 'name' and @@ -808,10 +787,10 @@ getWellGroupTargetProducer(const std::string& name, // part of. Later it is the accumulated factor including the group efficiency factor // of the child of group. OPM_TIMEFUNCTION(); - const Group::ProductionCMode& current_group_control = - this->groupState().production_control(group.name()); + const Group::ProductionCMode& current_group_control = this->groupState().production_control(group.name()); - if (current_group_control == Group::ProductionCMode::FLD || current_group_control == Group::ProductionCMode::NONE) { + if (current_group_control == Group::ProductionCMode::FLD + || current_group_control == Group::ProductionCMode::NONE) { // Return if we are not available for parent group. if (!group.productionGroupControlAvailable()) { return std::numeric_limits::max(); @@ -819,11 +798,11 @@ getWellGroupTargetProducer(const std::string& name, // Otherwise: check production share of parent's control. const auto& parent_group = this->schedule_.getGroup(group.parent(), this->report_step_); return this->getWellGroupTargetProducer(name, - parent, - parent_group, - rates, - efficiency_factor * group.getGroupEfficiencyFactor(), - resv_coeff); + parent, + parent_group, + rates, + efficiency_factor * group.getGroupEfficiencyFactor(), + resv_coeff); } // This can be false for FLD-controlled groups, we must therefore @@ -841,34 +820,29 @@ getWellGroupTargetProducer(const std::string& name, if (this->groupState().has_grat_sales_target(group.name())) grat_target_from_sales = this->groupState().grat_sales_target(group.name()); - WGHelpers::TargetCalculator tcalc{ - current_group_control, - this->phase_usage_info_, - resv_coeff, - grat_target_from_sales, - group.name(), - this->groupState(), - group.has_gpmaint_control(current_group_control) - }; + WGHelpers::TargetCalculator tcalc {current_group_control, + this->phase_usage_info_, + resv_coeff, + grat_target_from_sales, + group.name(), + this->groupState(), + group.has_gpmaint_control(current_group_control)}; - WGHelpers::FractionCalculator fcalc{ - this->schedule_, - *this, - this->summary_state_, - this->report_step_, - &this->guide_rate_, - tcalc.guideTargetMode(), - true, - Phase::OIL - }; + WGHelpers::FractionCalculator fcalc {this->schedule_, + *this, + this->summary_state_, + this->report_step_, + &this->guide_rate_, + tcalc.guideTargetMode(), + true, + Phase::OIL}; auto local_fraction_lambda = [&](const std::string& child, const std::string& always_incluced_name) { return fcalc.localFraction(child, always_incluced_name); }; - auto local_reduction_lambda = [&](const std::string& group_name) - { - const std::vector& group_target_reductions = - this->groupState().production_reduction_rates(group_name); + auto local_reduction_lambda = [&](const std::string& group_name) { + const std::vector& group_target_reductions + = this->groupState().production_reduction_rates(group_name); return tcalc.calcModeRateFromRates(group_target_reductions); }; @@ -888,12 +862,10 @@ getWellGroupTargetProducer(const std::string& name, // we need to find out the level where the local reduction is applied std::size_t local_reduction_level = 0; for (std::size_t ii = 1; ii < num_ancestors; ++ii) { - const int num_gr_ctrl = this->groupControlledWells( - chain[ii], - /*always_included_child=*/"", - /*is_production_group=*/true, - /*injection_phase=*/Phase::OIL - ); + const int num_gr_ctrl = this->groupControlledWells(chain[ii], + /*always_included_child=*/"", + /*is_production_group=*/true, + /*injection_phase=*/Phase::OIL); if (this->guide_rate_.has(chain[ii]) && num_gr_ctrl > 0) { local_reduction_level = ii; } @@ -908,7 +880,8 @@ getWellGroupTargetProducer(const std::string& name, if (local_reduction_level >= ii) { target -= local_reduction_lambda(chain[ii]); } - // If we are under individual control we need to add the wells rate back at the level where it is included in the local reduction + // If we are under individual control we need to add the wells rate back at the level where it is + // included in the local reduction if (local_reduction_level == ii && !this->wellState().isProductionGrup(name)) { target += current_rate_available * efficiency_factor; } @@ -920,21 +893,21 @@ getWellGroupTargetProducer(const std::string& name, } } // Avoid negative target rates comming from too large local reductions. - return std::max(Scalar(0.0), target / efficiency_factor);; + return std::max(Scalar(0.0), target / efficiency_factor); + ; } -template +template GuideRate::RateVector -WellGroupHelper:: -getWellRateVector(const std::string& name) const +WellGroupHelper::getWellRateVector(const std::string& name) const { return this->getGuideRateVector_(this->wellState().currentWellRates(name)); } -template +template std::vector -WellGroupHelper:: -groupChainTopBot(const std::string& bottom, const std::string& top) const +WellGroupHelper::groupChainTopBot(const std::string& bottom, + const std::string& top) const { // Get initial parent, 'bottom' can be a well or a group. std::string parent; @@ -959,14 +932,15 @@ groupChainTopBot(const std::string& bottom, const std::string& top) const return chain; } -template -int WellGroupHelper:: -groupControlledWells(const std::string& group_name, - const std::string& always_included_child, - const bool is_production_group, - const Phase injection_phase) const +template +int +WellGroupHelper::groupControlledWells(const std::string& group_name, + const std::string& always_included_child, + const bool is_production_group, + const Phase injection_phase) const { - auto num_wells = is_production_group ? this->groupState().number_of_wells_under_group_control(group_name) + auto num_wells = is_production_group + ? this->groupState().number_of_wells_under_group_control(group_name) : this->groupState().number_of_wells_under_inj_group_control(group_name, injection_phase); if (this->schedule_.hasWell(always_included_child, this->report_step_)) { const bool isInGroup = this->isInGroupChainTopBot_(always_included_child, group_name); @@ -980,9 +954,9 @@ groupControlledWells(const std::string& group_name, return num_wells; } -template -void WellGroupHelper:: -setCmodeGroup(const Group& group, GroupState& group_state) const +template +void +WellGroupHelper::setCmodeGroup(const Group& group, GroupState& group_state) const { for (const std::string& group_name : group.groups()) { @@ -1001,8 +975,7 @@ setCmodeGroup(const Group& group, GroupState& group_state) const } const auto& events = this->schedule_[this->report_step_].wellgroup_events(); - if (group.isInjectionGroup() - && events.hasEvent(group.name(), ScheduleEvents::GROUP_INJECTION_UPDATE)) { + if (group.isInjectionGroup() && events.hasEvent(group.name(), ScheduleEvents::GROUP_INJECTION_UPDATE)) { for (Phase phase : all) { if (!group.hasInjectionControl(phase)) @@ -1013,8 +986,7 @@ setCmodeGroup(const Group& group, GroupState& group_state) const } } - if (group.isProductionGroup() - && events.hasEvent(group.name(), ScheduleEvents::GROUP_PRODUCTION_UPDATE)) { + if (group.isProductionGroup() && events.hasEvent(group.name(), ScheduleEvents::GROUP_PRODUCTION_UPDATE)) { const auto controls = group.productionControls(this->summary_state_); group_state.production_control(group.name(), controls.cmode); } @@ -1040,10 +1012,9 @@ setCmodeGroup(const Group& group, GroupState& group_state) const // // See: updateGpMaintTargetForGroups() for detailed rationale. -template +template Scalar -WellGroupHelper:: -sumSolventRates(const Group& group, const bool is_injector) const +WellGroupHelper::sumSolventRates(const Group& group, const bool is_injector) const { Scalar rate = 0.0; for (const std::string& group_name : group.groups()) { @@ -1057,7 +1028,7 @@ sumSolventRates(const Group& group, const bool is_injector) const if (!well_index.has_value()) continue; - if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + if (!this->wellState().wellIsOwned(well_index.value(), well_name)) // Only sum once { continue; } @@ -1072,8 +1043,8 @@ sumSolventRates(const Group& group, const bool is_injector) const if (ws.status == Well::Status::SHUT) continue; - const Scalar factor = well_ecl.getEfficiencyFactor() * - this->wellState()[well_ecl.name()].efficiency_scaling_factor; + const Scalar factor + = well_ecl.getEfficiencyFactor() * this->wellState()[well_ecl.name()].efficiency_scaling_factor; if (is_injector) rate += factor * ws.sum_solvent_rates(); else @@ -1082,30 +1053,25 @@ sumSolventRates(const Group& group, const bool is_injector) const return rate; } -template -Scalar WellGroupHelper:: -sumWellPhaseRates(bool res_rates, - const Opm::Group& group, - const int phase_pos, - const bool injector, - const bool network) const +template +Scalar +WellGroupHelper::sumWellPhaseRates(bool res_rates, + const Opm::Group& group, + const int phase_pos, + const bool injector, + const bool network) const { // Only obtain satellite rates once (on rank 0) Scalar rate = 0.0; if (this->wellState().isRank0() && (group.hasSatelliteProduction() || group.hasSatelliteInjection())) { if (injector) { rate = this->satelliteInjectionRate_( - this->schedule_[this->report_step_], - group, - phase_pos, - res_rates - ); + this->schedule_[this->report_step_], group, phase_pos, res_rates); } else { const auto rate_comp = this->selectRateComponent_(phase_pos); if (rate_comp.has_value()) { rate = this->satelliteProductionRate_( - this->schedule_[this->report_step_], group, *rate_comp, res_rates - ); + this->schedule_[this->report_step_], group, *rate_comp, res_rates); } } // Satellite groups have no sub groups/wells so we're done @@ -1123,7 +1089,7 @@ sumWellPhaseRates(bool res_rates, if (!well_index.has_value()) continue; - if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + if (!this->wellState().wellIsOwned(well_index.value(), well_name)) // Only sum once { continue; } @@ -1138,7 +1104,7 @@ sumWellPhaseRates(bool res_rates, continue; const Scalar factor = well_ecl.getEfficiencyFactor(network) - * this->wellState().well(well_index.value()).efficiency_scaling_factor; + * this->wellState().well(well_index.value()).efficiency_scaling_factor; if (res_rates) { const auto& well_rates = ws.reservoir_rates; if (injector) @@ -1156,20 +1122,20 @@ sumWellPhaseRates(bool res_rates, return rate; } -template +template Scalar -WellGroupHelper:: -sumWellResRates(const Group& group, const int phasePos, const bool injector) const +WellGroupHelper::sumWellResRates(const Group& group, + const int phasePos, + const bool injector) const { return this->sumWellPhaseRates(/*res_rates=*/true, group, phasePos, injector); } -template +template Scalar -WellGroupHelper:: -sumWellSurfaceRates(const Group& group, - const int phase_pos, - const bool injector) const +WellGroupHelper::sumWellSurfaceRates(const Group& group, + const int phase_pos, + const bool injector) const { return this->sumWellPhaseRates(/*res_rates=*/false, group, phase_pos, injector); } @@ -1196,22 +1162,22 @@ sumWellSurfaceRates(const Group& group, // undefined reference to // `WellGroupHelper<...>::updateGpMaintTargetForGroups>(const Group&, ...)` -template -int WellGroupHelper:: -updateGroupControlledWells( - const bool is_production_group, const Phase injection_phase, GroupState& group_state -) const +template +int +WellGroupHelper::updateGroupControlledWells(const bool is_production_group, + const Phase injection_phase, + GroupState& group_state) const { OPM_TIMEFUNCTION(); const auto& group_name = "FIELD"; return this->updateGroupControlledWellsRecursive_( - group_name, is_production_group, injection_phase, group_state - ); + group_name, is_production_group, injection_phase, group_state); } -template -void WellGroupHelper:: -updateGroupProductionRates(const Group& group, GroupState& group_state) const +template +void +WellGroupHelper::updateGroupProductionRates(const Group& group, + GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1222,41 +1188,39 @@ updateGroupProductionRates(const Group& group, GroupState& group_state) std::vector rates(np, 0.0); for (int phase = 0; phase < np; ++phase) { rates[phase] = this->sumWellPhaseRates( - /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false - ); + /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false); } group_state.update_production_rates(group.name(), rates); } -template -void WellGroupHelper:: -updateGroupTargetReduction( - const Group& group, const bool is_injector, GroupState& group_state -) const +template +void +WellGroupHelper::updateGroupTargetReduction(const Group& group, + const bool is_injector, + GroupState& group_state) const { OPM_TIMEFUNCTION(); std::vector group_target_reduction(this->wellState().numPhases(), 0.0); - this->updateGroupTargetReductionRecursive_( - group, is_injector, group_target_reduction, group_state - ); + this->updateGroupTargetReductionRecursive_(group, is_injector, group_target_reduction, group_state); } -template -void WellGroupHelper:: -updateNetworkLeafNodeProductionRates(GroupState& group_state) const +template +void +WellGroupHelper::updateNetworkLeafNodeProductionRates( + GroupState& group_state) const { const auto& network = this->schedule_[this->report_step_].network(); if (network.active()) { const int np = this->wellState().numPhases(); for (const auto& group_name : network.leaf_nodes()) { std::vector network_rates(np, 0.0); - if (this->schedule_[this->report_step_].groups.has(group_name)) { // Allow empty leaf nodes that are not groups + if (this->schedule_[this->report_step_].groups.has( + group_name)) { // Allow empty leaf nodes that are not groups const auto& group = this->schedule_[this->report_step_].groups.get(group_name); if (group.numWells() > 0) { for (int phase = 0; phase < np; ++phase) { network_rates[phase] = this->sumWellPhaseRates( - /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/true - ); + /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/true); } } } @@ -1265,9 +1229,11 @@ updateNetworkLeafNodeProductionRates(GroupState& group_state) const } } -template -void WellGroupHelper:: -updateREINForGroups(const Group& group, bool sum_rank, GroupState& group_state) const +template +void +WellGroupHelper::updateREINForGroups(const Group& group, + bool sum_rank, + GroupState& group_state) const { OPM_TIMEFUNCTION(); const int np = this->wellState().numPhases(); @@ -1279,8 +1245,7 @@ updateREINForGroups(const Group& group, bool sum_rank, GroupState& group std::vector rein(np, 0.0); for (int phase = 0; phase < np; ++phase) { rein[phase] = this->sumWellPhaseRates( - /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false - ); + /*res_rates=*/false, group, phase, /*injector=*/false, /*network=*/false); } // add import rate and subtract consumption rate for group for gas @@ -1297,9 +1262,10 @@ updateREINForGroups(const Group& group, bool sum_rank, GroupState& group group_state.update_injection_rein_rates(group.name(), rein); } -template -void WellGroupHelper:: -updateReservoirRatesInjectionGroups(const Group& group, GroupState& group_state) const +template +void +WellGroupHelper::updateReservoirRatesInjectionGroups( + const Group& group, GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1310,23 +1276,24 @@ updateReservoirRatesInjectionGroups(const Group& group, GroupState& grou std::vector resv(np, 0.0); for (int phase = 0; phase < np; ++phase) { resv[phase] = this->sumWellPhaseRates( - /*res_rates=*/true, group, phase, /*injector=*/true, /*network=*/false - ); + /*res_rates=*/true, group, phase, /*injector=*/true, /*network=*/false); } group_state.update_injection_reservoir_rates(group.name(), resv); } -template -void WellGroupHelper:: -updateState(WellState& well_state, GroupState& group_state) +template +void +WellGroupHelper::updateState(WellState& well_state, + GroupState& group_state) { this->well_state_ = &well_state; this->group_state_ = &group_state; } -template -void WellGroupHelper:: -updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_state) const +template +void +WellGroupHelper::updateSurfaceRatesInjectionGroups(const Group& group, + GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1341,15 +1308,15 @@ updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_ group, phase, /*injector=*/true, - /*network=*/false - ); + /*network=*/false); } group_state.update_injection_surface_rates(group.name(), rates); } -template -void WellGroupHelper:: -updateVREPForGroups(const Group& group, GroupState& group_state) const +template +void +WellGroupHelper::updateVREPForGroups(const Group& group, + GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1360,19 +1327,16 @@ updateVREPForGroups(const Group& group, GroupState& group_state) const Scalar resv = 0.0; for (int phase = 0; phase < np; ++phase) { resv += this->sumWellPhaseRates( - /*res_rates=*/true, group, phase, /*injector=*/false, /*network=*/false - ); + /*res_rates=*/true, group, phase, /*injector=*/false, /*network=*/false); } group_state.update_injection_vrep_rate(group.name(), resv); } -template -void WellGroupHelper:: -updateWellRates( - const Group& group, - const WellState& well_state_nupcol, - WellState& well_state -) const +template +void +WellGroupHelper::updateWellRates(const Group& group, + const WellState& well_state_nupcol, + WellState& well_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1399,11 +1363,13 @@ updateWellRates( } } -template -void WellGroupHelper:: -updateWellRatesFromGroupTargetScale( - const Scalar scale, const Group& group, bool is_injector, WellState& well_state -) const +template +void +WellGroupHelper::updateWellRatesFromGroupTargetScale( + const Scalar scale, + const Group& group, + bool is_injector, + WellState& well_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -1411,17 +1377,17 @@ updateWellRatesFromGroupTargetScale( if (is_injector) { const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS}; for (Phase phase : all) { - const Group::InjectionCMode& current_group_control = - this->groupState().injection_control(group_name, phase); - individual_control = individual_control || - (current_group_control != Group::InjectionCMode::FLD && - current_group_control != Group::InjectionCMode::NONE); + const Group::InjectionCMode& current_group_control + = this->groupState().injection_control(group_name, phase); + individual_control = individual_control + || (current_group_control != Group::InjectionCMode::FLD + && current_group_control != Group::InjectionCMode::NONE); } } else { - const Group::ProductionCMode& current_group_control = - this->groupState().production_control(group_name); + const Group::ProductionCMode& current_group_control + = this->groupState().production_control(group_name); individual_control = (current_group_control != Group::ProductionCMode::FLD - && current_group_control != Group::ProductionCMode::NONE); + && current_group_control != Group::ProductionCMode::NONE); } if (!individual_control) { const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_); @@ -1463,19 +1429,16 @@ updateWellRatesFromGroupTargetScale( } } -template +template std::pair, Scalar> -WellGroupHelper:: -worstOffendingWell(const Group& group, - const Group::ProductionCMode& offended_control, - const Parallel::Communication& comm) const +WellGroupHelper::worstOffendingWell(const Group& group, + const Group::ProductionCMode& offended_control, + const Parallel::Communication& comm) const { std::pair, Scalar> offending_well {std::nullopt, 0.0}; for (const std::string& child_group : group.groups()) { const auto& this_group = this->schedule_.getGroup(child_group, this->report_step_); - const auto & offending_well_this = this->worstOffendingWell( - this_group, offended_control, comm - ); + const auto& offending_well_this = this->worstOffendingWell(this_group, offended_control, comm); if (offending_well_this.second > offending_well.second) { offending_well = offending_well_this; } @@ -1487,91 +1450,87 @@ worstOffendingWell(const Group& group, Scalar violating_rate = 0.0; Scalar prefered_rate = 0.0; const auto& pu = this->phase_usage_info_; - if (well_index.has_value() && this->wellState().wellIsOwned(well_index.value(), child_well)) - { + if (well_index.has_value() && this->wellState().wellIsOwned(well_index.value(), child_well)) { const auto& ws = this->wellState().well(child_well); // Cannot use deferredLogger() accessor here since it's non-const and this method is const. // Direct pointer dereference is safe as the logger object itself is mutable. auto& deferred_logger = *this->deferred_logger_; - switch (offended_control){ - case Group::ProductionCMode::ORAT: { - const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - violating_rate = ws.surface_rates[oil_pos]; - break; - } - case Group::ProductionCMode::GRAT: { - const int gas_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - violating_rate = ws.surface_rates[gas_pos]; - break; - } - case Group::ProductionCMode::WRAT: { - const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - violating_rate = ws.surface_rates[water_pos]; - break; - } - case Group::ProductionCMode::LRAT: { - assert(pu.phaseIsActive(IndexTraits::oilPhaseIdx)); - assert(pu.phaseIsActive(IndexTraits::waterPhaseIdx)); - const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - violating_rate = ws.surface_rates[oil_pos] + - ws.surface_rates[water_pos]; - break; - } - case Group::ProductionCMode::RESV: - for (int p = 0; p < this->wellState().numPhases(); ++p) { - violating_rate += ws.reservoir_rates[p]; - } - break; - case Group::ProductionCMode::NONE: - break; - case Group::ProductionCMode::FLD: - break; - case Group::ProductionCMode::PRBL: - OPM_DEFLOG_THROW(std::runtime_error, - "Group " + group.name() + - " GroupProductionCMode PRBL not implemented", - deferred_logger); - break; - case Group::ProductionCMode::CRAT: - OPM_DEFLOG_THROW(std::runtime_error, - "Group " + group.name() + - " GroupProductionCMode CRAT not implemented", - deferred_logger); - break; + switch (offended_control) { + case Group::ProductionCMode::ORAT: { + const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + violating_rate = ws.surface_rates[oil_pos]; + break; } - const auto preferred_phase = - this->schedule_.getWell(child_well, this->report_step_).getPreferredPhase(); - switch (preferred_phase) { - case Phase::OIL: { - const int oil_pos = - this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - prefered_rate = ws.surface_rates[oil_pos]; - break; - } - case Phase::GAS: { - const int gas_pos = - this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - prefered_rate = ws.surface_rates[gas_pos]; - break; - } - case Phase::WATER: { - const int water_pos = - this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - prefered_rate = ws.surface_rates[water_pos]; - break; - } - default: - // No others supported. - break; + case Group::ProductionCMode::GRAT: { + const int gas_pos = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + violating_rate = ws.surface_rates[gas_pos]; + break; + } + case Group::ProductionCMode::WRAT: { + const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + violating_rate = ws.surface_rates[water_pos]; + break; + } + case Group::ProductionCMode::LRAT: { + assert(pu.phaseIsActive(IndexTraits::oilPhaseIdx)); + assert(pu.phaseIsActive(IndexTraits::waterPhaseIdx)); + const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + const int water_pos = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + violating_rate = ws.surface_rates[oil_pos] + ws.surface_rates[water_pos]; + break; + } + case Group::ProductionCMode::RESV: + for (int p = 0; p < this->wellState().numPhases(); ++p) { + violating_rate += ws.reservoir_rates[p]; } + break; + case Group::ProductionCMode::NONE: + break; + case Group::ProductionCMode::FLD: + break; + case Group::ProductionCMode::PRBL: + OPM_DEFLOG_THROW(std::runtime_error, + "Group " + group.name() + " GroupProductionCMode PRBL not implemented", + deferred_logger); + break; + case Group::ProductionCMode::CRAT: + OPM_DEFLOG_THROW(std::runtime_error, + "Group " + group.name() + " GroupProductionCMode CRAT not implemented", + deferred_logger); + break; + } + const auto preferred_phase + = this->schedule_.getWell(child_well, this->report_step_).getPreferredPhase(); + switch (preferred_phase) { + case Phase::OIL: { + const int oil_pos + = this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + prefered_rate = ws.surface_rates[oil_pos]; + break; + } + case Phase::GAS: { + const int gas_pos + = this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + prefered_rate = ws.surface_rates[gas_pos]; + break; + } + case Phase::WATER: { + const int water_pos + = this->phase_usage_info_.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + prefered_rate = ws.surface_rates[water_pos]; + break; + } + default: + // No others supported. + break; + } } violating_rate = comm.sum(violating_rate); - if (violating_rate < 0 ) { // only check producing wells + if (violating_rate < 0) { // only check producing wells prefered_rate = comm.sum(prefered_rate); Scalar fraction = prefered_rate < -1e-16 ? violating_rate / prefered_rate : 1.0; - if ( fraction > offending_well.second) { - offending_well = {child_well, fraction}; + if (fraction > offending_well.second) { + offending_well = {child_well, fraction}; } } } @@ -1582,14 +1541,14 @@ worstOffendingWell(const Group& group, // Private methods // --------------------------------------------------------------------- -template +template std::string -WellGroupHelper:: -controlGroup_(const Group& group) const +WellGroupHelper::controlGroup_(const Group& group) const { const Group::ProductionCMode& currentGroupControl = this->groupState().production_control(group.name()); - if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) { + if (currentGroupControl == Group::ProductionCMode::FLD + || currentGroupControl == Group::ProductionCMode::NONE) { const auto& parent_name = group.control_group(); if (parent_name) { const auto& parent = this->schedule_.getGroup(parent_name.value(), this->report_step_); @@ -1600,10 +1559,9 @@ controlGroup_(const Group& group) const return group.name(); } -template +template Opm::GuideRate::RateVector -WellGroupHelper:: -getGuideRateVector_(const std::vector& rates) const +WellGroupHelper::getGuideRateVector_(const std::vector& rates) const { const auto& pu = this->phase_usage_info_; Scalar oilRate = 0.0; @@ -1624,10 +1582,10 @@ getGuideRateVector_(const std::vector& rates) const return {oilRate, gasRate, waterRate}; } -template +template bool -WellGroupHelper:: -isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const +WellGroupHelper::isInGroupChainTopBot_(const std::string& bottom, + const std::string& top) const { // Get initial parent, 'bottom' can be a well or a group. std::string parent; @@ -1648,9 +1606,9 @@ isInGroupChainTopBot_(const std::string& bottom, const std::string& top) const return true; } -template -int WellGroupHelper:: -phaseToActivePhaseIdx_(const Phase phase) const +template +int +WellGroupHelper::phaseToActivePhaseIdx_(const Phase phase) const { const auto& pu = this->phase_usage_info_; int phase_pos = -1; @@ -1677,22 +1635,20 @@ phaseToActivePhaseIdx_(const Phase phase) const return phase_pos; } -template -Scalar WellGroupHelper:: -satelliteInjectionRate_(const ScheduleState& sched, - const Group& group, - const int phase_pos, - bool res_rates) const +template +Scalar +WellGroupHelper::satelliteInjectionRate_(const ScheduleState& sched, + const Group& group, + const int phase_pos, + bool res_rates) const { Scalar rate = 0.0; const auto& pu = this->phase_usage_info_; if (group.hasSatelliteInjection()) { std::optional ph; - for (const auto& [bo_phase, phase] : std::array { - std::pair( IndexTraits::waterPhaseIdx, Phase::WATER), - std::pair( IndexTraits::oilPhaseIdx, Phase::OIL), - std::pair( IndexTraits::gasPhaseIdx, Phase::GAS) }) - { + for (const auto& [bo_phase, phase] : std::array {std::pair(IndexTraits::waterPhaseIdx, Phase::WATER), + std::pair(IndexTraits::oilPhaseIdx, Phase::OIL), + std::pair(IndexTraits::gasPhaseIdx, Phase::GAS)}) { if (pu.phaseIsActive(bo_phase) && (pu.canonicalToActivePhaseIdx(bo_phase) == phase_pos)) { ph = phase; } @@ -1706,7 +1662,7 @@ satelliteInjectionRate_(const ScheduleState& sched, if (const auto& qs = satrates.surface(); qs.has_value()) { rate = *qs; } - // We don't support reservoir rates for satellite injection groups + // We don't support reservoir rates for satellite injection groups } } } @@ -1716,11 +1672,11 @@ satelliteInjectionRate_(const ScheduleState& sched, template Scalar -WellGroupHelper:: -satelliteProductionRate_(const ScheduleState& sched, - const Group& group, - const GSatProd::GSatProdGroupProp::Rate rate_comp, - bool res_rates) const +WellGroupHelper::satelliteProductionRate_( + const ScheduleState& sched, + const Group& group, + const GSatProd::GSatProdGroupProp::Rate rate_comp, + bool res_rates) const { Scalar rate = 0.0; if (group.hasSatelliteProduction()) { @@ -1733,20 +1689,18 @@ satelliteProductionRate_(const ScheduleState& sched, return rate; } -template +template std::optional -WellGroupHelper:: -selectRateComponent_(const int phase_pos) const +WellGroupHelper::selectRateComponent_(const int phase_pos) const { - // TODO: this function can be wrong, phase_pos is not used anymore, this function requries checking and refactoring. + // TODO: this function can be wrong, phase_pos is not used anymore, this function requries checking and + // refactoring. const auto& pu = this->phase_usage_info_; using Rate = GSatProd::GSatProdGroupProp::Rate; - for (const auto& [phase, rate_comp] : std::array { - std::pair { IndexTraits::waterPhaseIdx, Rate::Water }, - std::pair { IndexTraits::oilPhaseIdx, Rate::Oil }, - std::pair { IndexTraits::gasPhaseIdx, Rate::Gas } }) - { + for (const auto& [phase, rate_comp] : std::array {std::pair {IndexTraits::waterPhaseIdx, Rate::Water}, + std::pair {IndexTraits::oilPhaseIdx, Rate::Oil}, + std::pair {IndexTraits::gasPhaseIdx, Rate::Gas}}) { if (pu.phaseIsActive(phase)) { const auto active_phase_pos = pu.canonicalToActivePhaseIdx(phase); if (active_phase_pos == phase_pos) { @@ -1758,14 +1712,13 @@ selectRateComponent_(const int phase_pos) const return std::nullopt; } -template -int WellGroupHelper:: -updateGroupControlledWellsRecursive_( +template +int +WellGroupHelper::updateGroupControlledWellsRecursive_( const std::string& group_name, const bool is_production_group, const Phase injection_phase, - GroupState& group_state -) const + GroupState& group_state) const { const Group& group = this->schedule_.getGroup(group_name, this->report_step_); int num_wells = 0; @@ -1774,21 +1727,20 @@ updateGroupControlledWellsRecursive_( bool included = false; if (is_production_group) { const auto ctrl = group_state.production_control(child_group); - included = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE); + included + = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE); } else { const auto ctrl = group_state.injection_control(child_group, injection_phase); - included = included || (ctrl == Group::InjectionCMode::FLD) - || (ctrl == Group::InjectionCMode::NONE); + included + = included || (ctrl == Group::InjectionCMode::FLD) || (ctrl == Group::InjectionCMode::NONE); } if (included) { num_wells += this->updateGroupControlledWellsRecursive_( - child_group, is_production_group, injection_phase, group_state - ); + child_group, is_production_group, injection_phase, group_state); } else { this->updateGroupControlledWellsRecursive_( - child_group, is_production_group, injection_phase, group_state - ); + child_group, is_production_group, injection_phase, group_state); } } for (const std::string& child_well : group.wells()) { @@ -1799,25 +1751,24 @@ updateGroupControlledWellsRecursive_( } else if (!is_production_group && !well.isProducer()) { const auto& well_controls = well.injectionControls(this->summary_state_); auto injectorType = well_controls.injector_type; - if ( (injection_phase == Phase::WATER && injectorType == InjectorType::WATER ) || - (injection_phase == Phase::OIL && injectorType == InjectorType::OIL ) || - (injection_phase == Phase::GAS && injectorType == InjectorType::GAS )) - { - included = included || this->wellState().isInjectionGrup(child_well); - } + if ((injection_phase == Phase::WATER && injectorType == InjectorType::WATER) + || (injection_phase == Phase::OIL && injectorType == InjectorType::OIL) + || (injection_phase == Phase::GAS && injectorType == InjectorType::GAS)) { + included = included || this->wellState().isInjectionGrup(child_well); + } } const auto ctrl1 = group_state.production_control(group.name()); - if (group.as_choke() && ((ctrl1 == Group::ProductionCMode::FLD) || - (ctrl1 == Group::ProductionCMode::NONE))) { + if (group.as_choke() + && ((ctrl1 == Group::ProductionCMode::FLD) || (ctrl1 == Group::ProductionCMode::NONE))) { // The auto choke group has not own group control but inherits control from an ancestor group. - // Number of wells should be calculated as zero when wells of auto choke group do not deliver target. - // This behaviour is then similar to no-autochoke group with wells not on GRUP control. + // Number of wells should be calculated as zero when wells of auto choke group do not deliver + // target. This behaviour is then similar to no-autochoke group with wells not on GRUP control. // The rates of these wells are summed up. The parent group target is reduced with this rate. // This reduced target becomes the target of the other child group of this parent. const auto num_phases = this->wellState().numPhases(); std::vector rates(num_phases, 0.0); for (int phase_pos = 0; phase_pos < num_phases; ++phase_pos) { - rates[phase_pos] = this->sumWellSurfaceRates(group, phase_pos, /*injector=*/false); + rates[phase_pos] = this->sumWellSurfaceRates(group, phase_pos, /*injector=*/false); } // Get the ancestor of the auto choke group that has group control (cmode != FLD, NONE) @@ -1835,26 +1786,26 @@ updateGroupControlledWellsRecursive_( grat_target_from_sales = group_state.grat_sales_target(control_group_name); std::vector resv_coeff(num_phases, 1.0); - WGHelpers::TargetCalculator tcalc{ + WGHelpers::TargetCalculator tcalc { control_group_cmode, this->phase_usage_info_, resv_coeff, grat_target_from_sales, group.name(), group_state, - group.has_gpmaint_control(control_group_cmode) - }; + group.has_gpmaint_control(control_group_cmode)}; const auto& control_group_target = tcalc.groupTarget(ctrl, this->deferredLogger()); // Calculates the guide rate of the parent group with control. - // It is allowed that the guide rate of this group is defaulted. The guide rate will be derived from the children groups - const auto& control_group_guide_rate = this->getGuideRate( - control_group_name, tcalc.guideTargetMode() - ); + // It is allowed that the guide rate of this group is defaulted. The guide rate will be + // derived from the children groups + const auto& control_group_guide_rate + = this->getGuideRate(control_group_name, tcalc.guideTargetMode()); if (control_group_guide_rate > 0) { // Target rate for the auto choke group - const Scalar target_rate = control_group_target * group_guide_rate / control_group_guide_rate; + const Scalar target_rate + = control_group_target * group_guide_rate / control_group_guide_rate; const Scalar current_rate = tcalc.calcModeRateFromRates(rates); if (current_rate < target_rate) @@ -1870,30 +1821,26 @@ updateGroupControlledWellsRecursive_( if (is_production_group) { group_state.update_number_of_wells_under_group_control(group_name, num_wells); } else { - group_state.update_number_of_wells_under_inj_group_control( - group_name, injection_phase, num_wells - ); + group_state.update_number_of_wells_under_inj_group_control(group_name, injection_phase, num_wells); } return num_wells; } -template -void WellGroupHelper:: -updateGroupTargetReductionRecursive_( +template +void +WellGroupHelper::updateGroupTargetReductionRecursive_( const Group& group, const bool is_injector, std::vector& group_target_reduction, - GroupState& group_state -) const + GroupState& group_state) const { const int np = this->wellState().numPhases(); for (const std::string& sub_group_name : group.groups()) { std::vector sub_group_target_reduction(np, 0.0); const Group& sub_group = this->schedule_.getGroup(sub_group_name, this->report_step_); this->updateGroupTargetReductionRecursive_( - sub_group, is_injector, sub_group_target_reduction, group_state - ); + sub_group, is_injector, sub_group_target_reduction, group_state); const Scalar sub_group_efficiency = sub_group.getGroupEfficiencyFactor(); @@ -1906,52 +1853,47 @@ updateGroupTargetReductionRecursive_( if (phase_pos == -1) continue; const Group::InjectionCMode& current_group_control - = group_state.injection_control(sub_group.name(), phase); + = group_state.injection_control(sub_group.name(), phase); const bool individual_control = (current_group_control != Group::InjectionCMode::FLD - && current_group_control != Group::InjectionCMode::NONE); - const int num_group_controlled_wells = this->groupControlledWells( - sub_group.name(), - /*always_included_child=*/"", - !is_injector, - phase - ); + && current_group_control != Group::InjectionCMode::NONE); + const int num_group_controlled_wells + = this->groupControlledWells(sub_group.name(), + /*always_included_child=*/"", + !is_injector, + phase); if (individual_control || num_group_controlled_wells == 0) { - group_target_reduction[phase_pos] += sub_group_efficiency * - this->sumWellSurfaceRates(sub_group, phase_pos, is_injector); - } - else { + group_target_reduction[phase_pos] += sub_group_efficiency + * this->sumWellSurfaceRates(sub_group, phase_pos, is_injector); + } else { // Accumulate from this subgroup only if no group guide rate is set for it. if (!this->guide_rate_.has(sub_group.name(), phase)) { - group_target_reduction[phase_pos] += sub_group_efficiency * - sub_group_target_reduction[phase_pos]; + group_target_reduction[phase_pos] + += sub_group_efficiency * sub_group_target_reduction[phase_pos]; } } } - } - else { - const Group::ProductionCMode& current_group_control = - group_state.production_control(sub_group.name()); + } else { + const Group::ProductionCMode& current_group_control + = group_state.production_control(sub_group.name()); const bool individual_control = (current_group_control != Group::ProductionCMode::FLD && current_group_control != Group::ProductionCMode::NONE); - const int num_group_controlled_wells = this->groupControlledWells( - sub_group.name(), - /*always_included_child=*/"", - !is_injector, - /*injection_phase_not_used=*/Phase::OIL - ); + const int num_group_controlled_wells + = this->groupControlledWells(sub_group.name(), + /*always_included_child=*/"", + !is_injector, + /*injection_phase_not_used=*/Phase::OIL); if (individual_control || num_group_controlled_wells == 0) { for (int phase = 0; phase < np; phase++) { group_target_reduction[phase] += sub_group_efficiency * this->sumWellSurfaceRates(sub_group, phase, is_injector); } - } - else { + } else { // The subgroup may participate in group control. - if (!this->guide_rate_.has(sub_group.name())) { + if (!this->guide_rate_.has(sub_group.name())) { // Accumulate from this subgroup only if no group guide rate is set for it. for (int phase = 0; phase < np; phase++) { - group_target_reduction[phase] += sub_group_efficiency * - sub_group_target_reduction[phase]; + group_target_reduction[phase] + += sub_group_efficiency * sub_group_target_reduction[phase]; } } } @@ -1971,7 +1913,7 @@ updateGroupTargetReductionRecursive_( if (!well_index.has_value()) continue; - if (! this->wellState().wellIsOwned(well_index.value(), well_name) ) // Only sum once + if (!this->wellState().wellIsOwned(well_index.value(), well_name)) // Only sum once { continue; } @@ -1980,8 +1922,8 @@ updateGroupTargetReductionRecursive_( if (ws.status == Well::Status::SHUT) continue; - const Scalar efficiency = well_tmp.getEfficiencyFactor() * - this->wellState().well(well_index.value()).efficiency_scaling_factor; + const Scalar efficiency = well_tmp.getEfficiencyFactor() + * this->wellState().well(well_index.value()).efficiency_scaling_factor; // add contribution from wells not under group control if (is_injector) { @@ -1989,21 +1931,19 @@ updateGroupTargetReductionRecursive_( for (int phase = 0; phase < np; phase++) { group_target_reduction[phase] += ws.surface_rates[phase] * efficiency; } - } - else { - if ((ws.production_cmode != Well::ProducerCMode::GRUP)){ - if (!group.as_choke()) { - for (int phase = 0; phase < np; phase++) { - group_target_reduction[phase] -= ws.surface_rates[phase] * efficiency; - } + } else { + if ((ws.production_cmode != Well::ProducerCMode::GRUP)) { + if (!group.as_choke()) { + for (int phase = 0; phase < np; phase++) { + group_target_reduction[phase] -= ws.surface_rates[phase] * efficiency; + } } } } } if (is_injector) { group_state.update_injection_reduction_rates(group.name(), group_target_reduction); - } - else { + } else { group_state.update_production_reduction_rates(group.name(), group_target_reduction); } } diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index d57f6a9bfb..2ca3209d2e 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -21,8 +21,8 @@ #include #include -#include #include +#include #include #include #include @@ -38,23 +38,27 @@ #include #include -namespace Opm { +namespace Opm +{ -template -class WellGroupHelper { +template +class WellGroupHelper +{ public: // RAII guard for temporarily setting wellstate pointer - class WellStateGuard { + class WellStateGuard + { public: WellStateGuard(WellGroupHelper& wgHelper, WellState& well_state) - : wgHelper_{wgHelper} - , previous_state_ptr_{wgHelper_.well_state_} + : wgHelper_ {wgHelper} + , previous_state_ptr_ {wgHelper_.well_state_} { // Set the new state directly wgHelper_.well_state_ = &well_state; } - ~WellStateGuard() { + ~WellStateGuard() + { // Restore the previous state wgHelper_.well_state_ = previous_state_ptr_; } @@ -71,94 +75,87 @@ public: }; // RAII guard for temporarily setting groupstate pointer - class GroupStateGuard { - public: - GroupStateGuard(WellGroupHelper& wgHelper, GroupState& group_state) - : wgHelper_{wgHelper} - , previous_state_ptr_{wgHelper_.group_state_} - { - // Set the new state directly - wgHelper_.group_state_ = &group_state; - } + class GroupStateGuard + { + public: + GroupStateGuard(WellGroupHelper& wgHelper, GroupState& group_state) + : wgHelper_ {wgHelper} + , previous_state_ptr_ {wgHelper_.group_state_} + { + // Set the new state directly + wgHelper_.group_state_ = &group_state; + } - ~GroupStateGuard() { - // Restore the previous state - wgHelper_.group_state_ = previous_state_ptr_; - } + ~GroupStateGuard() + { + // Restore the previous state + wgHelper_.group_state_ = previous_state_ptr_; + } - // Delete copy and move operations - GroupStateGuard(const GroupStateGuard&) = delete; - GroupStateGuard& operator=(const GroupStateGuard&) = delete; - GroupStateGuard(GroupStateGuard&&) = delete; - GroupStateGuard& operator=(GroupStateGuard&&) = delete; + // Delete copy and move operations + GroupStateGuard(const GroupStateGuard&) = delete; + GroupStateGuard& operator=(const GroupStateGuard&) = delete; + GroupStateGuard(GroupStateGuard&&) = delete; + GroupStateGuard& operator=(GroupStateGuard&&) = delete; - private: - WellGroupHelper& wgHelper_; - const GroupState* previous_state_ptr_; - }; + private: + WellGroupHelper& wgHelper_; + const GroupState* previous_state_ptr_; + }; - WellGroupHelper( - WellState& well_state, - GroupState& group_state, - const Schedule& schedule, - const SummaryState& summary_state, - const GuideRate& guide_rate, - const PhaseUsageInfo& phase_usage_info - ); + WellGroupHelper(WellState& well_state, + GroupState& group_state, + const Schedule& schedule, + const SummaryState& summary_state, + const GuideRate& guide_rate, + const PhaseUsageInfo& phase_usage_info); void accumulateGroupEfficiencyFactor(const Group& group, Scalar& factor) const; - std::pair checkGroupConstraintsInj( - const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Phase injection_phase, - const Scalar efficiency_factor, - const std::vector& resv_coeff, - const bool check_guide_rate - ) const; + std::pair checkGroupConstraintsInj(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate) const; - std::pair checkGroupConstraintsProd( - const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Scalar efficiency_factor, - const std::vector& resv_coeff, - const bool check_guide_rate - ) const; + std::pair checkGroupConstraintsProd(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff, + const bool check_guide_rate) const; - std::map computeNetworkPressures( - const Network::ExtNetwork& network, - const VFPProdProperties& vfp_prod_props, - const Parallel::Communication& comm - ) const; + std::map computeNetworkPressures(const Network::ExtNetwork& network, + const VFPProdProperties& vfp_prod_props, + const Parallel::Communication& comm) const; - DeferredLogger& deferredLogger() const { return *this->deferred_logger_; } + DeferredLogger& deferredLogger() const + { + return *this->deferred_logger_; + } Scalar getGuideRate(const std::string& name, const GuideRateModel::Target target) const; GuideRate::RateVector getProductionGroupRateVector(const std::string& group_name) const; - Scalar getWellGroupTargetInjector( - const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Phase injection_phase, - const Scalar efficiency_factor, - const std::vector& resv_coeff - ) const; + Scalar getWellGroupTargetInjector(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Phase injection_phase, + const Scalar efficiency_factor, + const std::vector& resv_coeff) const; - Scalar getWellGroupTargetProducer( - const std::string& name, - const std::string& parent, - const Group& group, - const Scalar* rates, - const Scalar efficiency_factor, - const std::vector& resv_coeff - ) const; + Scalar getWellGroupTargetProducer(const std::string& name, + const std::string& parent, + const Group& group, + const Scalar* rates, + const Scalar efficiency_factor, + const std::vector& resv_coeff) const; GuideRate::RateVector getWellRateVector(const std::string& name) const; @@ -166,37 +163,49 @@ public: /// returns the number of wells that are actively under group control for a given group with name given /// by group_name - int groupControlledWells( - const std::string& group_name, - const std::string& always_included_child, - const bool is_production_group, - const Phase injection_phase - ) const; + int groupControlledWells(const std::string& group_name, + const std::string& always_included_child, + const bool is_production_group, + const Phase injection_phase) const; - const GroupState& groupState() const { return *this->group_state_; } + const GroupState& groupState() const + { + return *this->group_state_; + } - const PhaseUsageInfo& phaseUsageInfo() const { return this->phase_usage_info_; } + const PhaseUsageInfo& phaseUsageInfo() const + { + return this->phase_usage_info_; + } - GroupStateGuard pushGroupState(GroupState& group_state) { + GroupStateGuard pushGroupState(GroupState& group_state) + { return GroupStateGuard(*this, group_state); } - WellStateGuard pushWellState(WellState& well_state) { + WellStateGuard pushWellState(WellState& well_state) + { return WellStateGuard(*this, well_state); } void setCmodeGroup(const Group& group, GroupState& group_state) const; - void setLogger(DeferredLogger* deferred_logger) { deferred_logger_ = deferred_logger; } + void setLogger(DeferredLogger* deferred_logger) + { + deferred_logger_ = deferred_logger; + } template - void setRegionAveragePressureCalculator( - const Group& group, - const FieldPropsManager& fp, - std::map>& regional_average_pressure_calculator - ) const; + void + setRegionAveragePressureCalculator(const Group& group, + const FieldPropsManager& fp, + std::map>& + regional_average_pressure_calculator) const; - void setReportStep(int report_step) { report_step_ = report_step; } + void setReportStep(int report_step) + { + report_step_ = report_step; + } Scalar sumSolventRates(const Group& group, const bool is_injector) const; @@ -204,32 +213,29 @@ public: Scalar sumWellSurfaceRates(const Group& group, const int phase_pos, const bool injector) const; - Scalar sumWellPhaseRates( - bool res_rates, - const Group& group, - const int phase_pos, - const bool injector, - const bool network = false - ) const; + Scalar sumWellPhaseRates(bool res_rates, + const Group& group, + const int phase_pos, + const bool injector, + const bool network = false) const; - template void updateGpMaintTargetForGroups( - const Group& group, - const RegionalValues& regional_values, - const double dt, - GroupState& group_state - ) const; + template + void updateGpMaintTargetForGroups(const Group& group, + const RegionalValues& regional_values, + const double dt, + GroupState& group_state) const; - /// update the number of wells that are actively under group control for a given group with name given by group_name - /// its main usage is to detect cases where there is no wells under group control - int updateGroupControlledWells( - const bool is_production_group, const Phase injection_phase, GroupState& group_state - ) const; + /// update the number of wells that are actively under group control for a given group with name given by + /// group_name its main usage is to detect cases where there is no wells under group control + int updateGroupControlledWells(const bool is_production_group, + const Phase injection_phase, + GroupState& group_state) const; void updateGroupProductionRates(const Group& group, GroupState& group_state) const; - void updateGroupTargetReduction( - const Group& group, const bool is_injector, GroupState& group_state - ) const; + void updateGroupTargetReduction(const Group& group, + const bool is_injector, + GroupState& group_state) const; void updateNetworkLeafNodeProductionRates(GroupState& group_state) const; @@ -239,30 +245,30 @@ public: void updateVREPForGroups(const Group& group, GroupState& group_state) const; - void updateState( - WellState& well_state, GroupState& group_state - ); + void updateState(WellState& well_state, GroupState& group_state); void updateSurfaceRatesInjectionGroups(const Group& group, GroupState& group_state) const; - void updateWellRates( - const Group& group, - const WellState& well_state_nupcol, - WellState& well_state - ) const; + void updateWellRates(const Group& group, + const WellState& well_state_nupcol, + WellState& well_state) const; - const WellState& wellState() const { return *this->well_state_; } + const WellState& wellState() const + { + return *this->well_state_; + } - void updateWellRatesFromGroupTargetScale( - const Scalar scale, const Group& group, bool is_injector, WellState& well_state - ) const; + void updateWellRatesFromGroupTargetScale(const Scalar scale, + const Group& group, + bool is_injector, + WellState& well_state) const; /// Returns the name of the worst offending well and its fraction (i.e. violated_phase / preferred_phase) - std::pair, Scalar> worstOffendingWell( - const Group& group, - const Group::ProductionCMode& offended_control, - const Parallel::Communication& comm - ) const; + std::pair, Scalar> + worstOffendingWell(const Group& group, + const Group::ProductionCMode& offended_control, + const Parallel::Communication& comm) const; + private: std::string controlGroup_(const Group& group) const; @@ -273,43 +279,35 @@ private: int phaseToActivePhaseIdx_(const Phase phase) const; - Scalar satelliteInjectionRate_( - const ScheduleState& sched, - const Group& group, - const int phase_pos, - bool res_rates - ) const; + Scalar satelliteInjectionRate_(const ScheduleState& sched, + const Group& group, + const int phase_pos, + bool res_rates) const; - Scalar satelliteProductionRate_( - const ScheduleState& sched, - const Group& group, - const GSatProd::GSatProdGroupProp::Rate rate_comp, - bool res_rates - ) const; + Scalar satelliteProductionRate_(const ScheduleState& sched, + const Group& group, + const GSatProd::GSatProdGroupProp::Rate rate_comp, + bool res_rates) const; std::optional selectRateComponent_(const int phase_pos) const; - int updateGroupControlledWellsRecursive_( - const std::string& group_name, - const bool is_production_group, - const Phase injection_phase, - GroupState& group_state - ) const; + int updateGroupControlledWellsRecursive_(const std::string& group_name, + const bool is_production_group, + const Phase injection_phase, + GroupState& group_state) const; - void updateGroupTargetReductionRecursive_( - const Group& group, - const bool is_injector, - std::vector& group_target_reduction, - GroupState& group_state - ) const; + void updateGroupTargetReductionRecursive_(const Group& group, + const bool is_injector, + std::vector& group_target_reduction, + GroupState& group_state) const; - const WellState* well_state_{nullptr}; - const GroupState* group_state_{nullptr}; + const WellState* well_state_ {nullptr}; + const GroupState* group_state_ {nullptr}; const Schedule& schedule_; const SummaryState& summary_state_; const GuideRate& guide_rate_; - int report_step_{0}; - DeferredLogger* deferred_logger_{nullptr}; + int report_step_ {0}; + DeferredLogger* deferred_logger_ {nullptr}; // NOTE: The phase usage info seems to be read-only throughout the simulation, so it should be safe // to store a reference to it here. const PhaseUsageInfo& phase_usage_info_; @@ -324,17 +322,19 @@ private: // not available when WellGroupHelper.cpp is compiled. Template functions // must be visible at their instantiation point. // See WellGroupHelper.cpp for detailed rationale. -template +template template -void WellGroupHelper:: -setRegionAveragePressureCalculator( +void +WellGroupHelper::setRegionAveragePressureCalculator( const Group& group, const FieldPropsManager& fp, - std::map>& regional_average_pressure_calculator -) const + std::map>& regional_average_pressure_calculator) + const { for (const std::string& groupName : group.groups()) { - this->setRegionAveragePressureCalculator(this->schedule_.getGroup(groupName, this->report_step_), fp, regional_average_pressure_calculator); + this->setRegionAveragePressureCalculator(this->schedule_.getGroup(groupName, this->report_step_), + fp, + regional_average_pressure_calculator); } const auto& gpm = group.gpmaint(); if (!gpm) @@ -347,8 +347,8 @@ setRegionAveragePressureCalculator( if (regional_average_pressure_calculator.count(reg->first) == 0) { const std::string name = (reg->first.rfind("FIP", 0) == 0) ? reg->first : "FIP" + reg->first; const auto& fipnum = fp.get_int(name); - regional_average_pressure_calculator[reg->first] = - std::make_unique(fipnum); + regional_average_pressure_calculator[reg->first] + = std::make_unique(fipnum); } } @@ -357,14 +357,13 @@ setRegionAveragePressureCalculator( // not available when WellGroupHelper.cpp is compiled. Template functions // must be visible at their instantiation point. // See WellGroupHelper.cpp for detailed rationale. -template +template template -void WellGroupHelper:: -updateGpMaintTargetForGroups(const Group& group, - const RegionalValues& regional_values, - const double dt, - GroupState& group_state - ) const +void +WellGroupHelper::updateGpMaintTargetForGroups(const Group& group, + const RegionalValues& regional_values, + const double dt, + GroupState& group_state) const { OPM_TIMEFUNCTION(); for (const std::string& group_name : group.groups()) { @@ -386,63 +385,56 @@ updateGpMaintTargetForGroups(const Group& group, bool injection = true; Scalar sign = 1.0; switch (gpm->flow_target()) { - case GPMaint::FlowTarget::RESV_PROD: - { - current_rate = -this->groupState().injection_vrep_rate(group.name()); - injection = false; - sign = -1.0; - break; + case GPMaint::FlowTarget::RESV_PROD: { + current_rate = -this->groupState().injection_vrep_rate(group.name()); + injection = false; + sign = -1.0; + break; + } + case GPMaint::FlowTarget::RESV_OINJ: { + if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { + const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + current_rate = group_state.injection_reservoir_rates(group.name())[io]; } - case GPMaint::FlowTarget::RESV_OINJ: - { - if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { - const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - current_rate = group_state.injection_reservoir_rates(group.name())[io]; - } - break; + break; + } + case GPMaint::FlowTarget::RESV_WINJ: { + if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { + const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + current_rate = group_state.injection_reservoir_rates(group.name())[iw]; } - case GPMaint::FlowTarget::RESV_WINJ: - { - if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { - const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - current_rate = group_state.injection_reservoir_rates(group.name())[iw]; - } - break; + break; + } + case GPMaint::FlowTarget::RESV_GINJ: { + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + current_rate = group_state.injection_reservoir_rates(group.name())[ig]; } - case GPMaint::FlowTarget::RESV_GINJ: - { - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - current_rate = group_state.injection_reservoir_rates(group.name())[ig]; - } - break; + break; + } + case GPMaint::FlowTarget::SURF_OINJ: { + if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { + const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); + current_rate = group_state.injection_surface_rates(group.name())[io]; } - case GPMaint::FlowTarget::SURF_OINJ: - { - if (pu.phaseIsActive(IndexTraits::oilPhaseIdx)) { - const auto io = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); - current_rate = group_state.injection_surface_rates(group.name())[io]; - } - break; + break; + } + case GPMaint::FlowTarget::SURF_WINJ: { + if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { + const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); + current_rate = group_state.injection_surface_rates(group.name())[iw]; } - case GPMaint::FlowTarget::SURF_WINJ: - { - if (pu.phaseIsActive(IndexTraits::waterPhaseIdx)) { - const auto iw = pu.canonicalToActivePhaseIdx(IndexTraits::waterPhaseIdx); - current_rate = group_state.injection_surface_rates(group.name())[iw]; - } - break; + break; + } + case GPMaint::FlowTarget::SURF_GINJ: { + if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { + const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); + current_rate = group_state.injection_surface_rates(group.name())[ig]; } - case GPMaint::FlowTarget::SURF_GINJ: - { - if (pu.phaseIsActive(IndexTraits::gasPhaseIdx)) { - const auto ig = pu.canonicalToActivePhaseIdx(IndexTraits::gasPhaseIdx); - current_rate = group_state.injection_surface_rates(group.name())[ig]; - } - break; - } - default: - throw std::invalid_argument("Invalid Flow target type in GPMAINT"); + break; + } + default: + throw std::invalid_argument("Invalid Flow target type in GPMAINT"); } auto& gpmaint_state = group_state.gpmaint(group.name()); // we only activate gpmaint if pressure is lower than the target regional pressure for injectors @@ -454,7 +446,7 @@ updateGpMaintTargetForGroups(const Group& group, } else { gpm->resetState(gpmaint_state); } - group_state.update_gpmaint_target(group.name(), std::max(Scalar{0.0}, sign * rate)); + group_state.update_gpmaint_target(group.name(), std::max(Scalar {0.0}, sign * rate)); } } // namespace Opm From db1a3c344a5f4f3c0f5418eec40e1dbbcae72fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 15:37:20 +0200 Subject: [PATCH 20/26] Don't store non-const deferred logger object Don't store non-const DeferredLogger object in WellGroupHelper class, instead pass it explicitly as a non-const parameter when needed. --- .../wells/BlackoilWellModelConstraints.cpp | 2 +- .../wells/BlackoilWellModelGeneric.cpp | 16 +++-- .../wells/BlackoilWellModel_impl.hpp | 3 - opm/simulators/wells/WellGroupConstraints.cpp | 22 ++++--- opm/simulators/wells/WellGroupConstraints.hpp | 9 ++- opm/simulators/wells/WellGroupHelper.cpp | 60 +++++++++++-------- opm/simulators/wells/WellGroupHelper.hpp | 32 +++++----- .../wells/WellInterfaceFluidSystem.cpp | 7 ++- .../wells/WellInterfaceFluidSystem.hpp | 3 +- opm/simulators/wells/WellInterface_impl.hpp | 4 +- 10 files changed, 90 insertions(+), 68 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index d63feebf6c..be1b8bb470 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -452,7 +452,7 @@ actionOnBrokenConstraints(const Group& group, } case Group::ExceedAction::WELL: { std::tie(worst_offending_well, std::ignore) = - wgHelper().worstOffendingWell(group, newControl, wellModel_.comm()); + wgHelper().worstOffendingWell(group, newControl, wellModel_.comm(), deferred_logger); break; } case Group::ExceedAction::PLUG: { diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index a87f710871..a0c71536a8 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -695,7 +695,8 @@ checkGroupHigherConstraints(const Group& group, phase, group.getGroupEfficiencyFactor(), resv_coeff_inj, - /*check_guide_rate*/true + /*check_guide_rate*/true, + deferred_logger ); if (is_changed) { auto& group_log = switched_inj_groups_[group.name()][static_cast>(phase)]; @@ -759,7 +760,8 @@ checkGroupHigherConstraints(const Group& group, rates.data(), group.getGroupEfficiencyFactor(), resv_coeff, - /*check_guide_rate*/true + /*check_guide_rate*/true, + deferred_logger ); if (is_changed) { const auto group_limit_action = group.productionControls(summaryState_).group_limit_action; @@ -1343,7 +1345,9 @@ updateAndCommunicateGroupData(const int reportStepIdx, constexpr std::array is_production_group = {true, false, false, false}; constexpr std::array phases = { Phase::OIL, Phase::WATER, Phase::OIL, Phase::GAS }; for (int i = 0; i < num_configs; i++) { - wg_helper.updateGroupControlledWells(is_production_group[i], phases[i], this->groupState()); + wg_helper.updateGroupControlledWells( + is_production_group[i], phases[i], this->groupState(), deferred_logger + ); } } // the group target reduction rates needs to be update since wells may have switched to/from GRUP control @@ -1384,7 +1388,8 @@ updateAndCommunicateGroupData(const int reportStepIdx, group, ws.surface_rates.data(), efficiencyFactor, - resv_coeff + resv_coeff, + deferred_logger ); } else { const auto& well_controls = well->wellEcl().injectionControls(summaryState_); @@ -1416,7 +1421,8 @@ updateAndCommunicateGroupData(const int reportStepIdx, ws.surface_rates.data(), injectionPhase, efficiencyFactor, - resv_coeff + resv_coeff, + deferred_logger ); } auto& ws_update = this->wellState().well(well->indexOfWell()); diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 302f9d12ae..2a095012fc 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -196,7 +196,6 @@ namespace Opm { DeferredLogger local_deferredLogger{}; this->wgHelper().setReportStep(timeStepIdx); - this->wgHelper().setLogger(&local_deferredLogger); this->report_step_starts_ = true; this->report_step_start_events_ = this->schedule()[timeStepIdx].wellgroup_events(); @@ -333,7 +332,6 @@ namespace Opm { this->updateAverageFormationFactor(); DeferredLogger local_deferredLogger; - this->wgHelper().setLogger(&local_deferredLogger); this->switched_prod_groups_.clear(); this->switched_inj_groups_.clear(); @@ -1135,7 +1133,6 @@ namespace Opm { OPM_TIMEFUNCTION(); DeferredLogger local_deferredLogger; - this->wgHelper().setLogger(&local_deferredLogger); this->guide_rate_handler_.setLogger(&local_deferredLogger); if constexpr (BlackoilWellModelGasLift::glift_debug) { if (gaslift_.terminalOutput()) { diff --git a/opm/simulators/wells/WellGroupConstraints.cpp b/opm/simulators/wells/WellGroupConstraints.cpp index bb2e876e8a..c0cc0dd160 100644 --- a/opm/simulators/wells/WellGroupConstraints.cpp +++ b/opm/simulators/wells/WellGroupConstraints.cpp @@ -42,7 +42,8 @@ checkGroupConstraintsInj(const Group& group, const Scalar efficiencyFactor, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate) const + const bool check_guide_rate, + DeferredLogger& deferred_logger) const { const auto& well_state = wgHelper.wellState(); @@ -84,7 +85,8 @@ checkGroupConstraintsInj(const Group& group, injectionPhase, efficiencyFactor, resv_coeff, - check_guide_rate + check_guide_rate, + deferred_logger ); } @@ -95,7 +97,8 @@ checkGroupConstraintsProd(const Group& group, const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, const RateConvFunc& rateConverter, - const bool check_guide_rate) const + const bool check_guide_rate, + DeferredLogger& deferred_logger) const { const auto& well_state = wgHelper.wellState(); @@ -110,7 +113,8 @@ checkGroupConstraintsProd(const Group& group, ws.surface_rates.data(), efficiencyFactor, resv_coeff, - check_guide_rate + check_guide_rate, + deferred_logger ); } @@ -121,7 +125,8 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, const SummaryState& summaryState, const RateConvFunc& rateConverter, const bool check_guide_rate, - WellStateType& well_state) const + WellStateType& well_state, + DeferredLogger& deferred_logger) const { const auto& well = well_.wellEcl(); const int well_index = well_.indexOfWell(); @@ -144,7 +149,8 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, this->checkGroupConstraintsInj(group, wgHelper, efficiencyFactor, summaryState, rateConverter, - check_guide_rate); + check_guide_rate, + deferred_logger); // If a group constraint was broken, we set the current well control to // be GRUP. if (group_constraint.first) { @@ -174,7 +180,9 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, const std::pair group_constraint = this->checkGroupConstraintsProd(group, wgHelper, efficiencyFactor, - rateConverter, check_guide_rate); + rateConverter, + check_guide_rate, + deferred_logger); // If a group constraint was broken, we set the current well control to // be GRUP. if (group_constraint.first) { diff --git a/opm/simulators/wells/WellGroupConstraints.hpp b/opm/simulators/wells/WellGroupConstraints.hpp index c13036d294..85b8a3811c 100644 --- a/opm/simulators/wells/WellGroupConstraints.hpp +++ b/opm/simulators/wells/WellGroupConstraints.hpp @@ -63,7 +63,8 @@ public: const SummaryState& summaryState, const RateConvFunc& rateConverter, const bool check_guide_rate, - WellStateType& well_state) const; + WellStateType& well_state, + DeferredLogger& deferred_logger) const; private: std::pair @@ -72,14 +73,16 @@ private: const Scalar efficiencyFactor, const SummaryState& summaryState, const RateConvFunc& rateConverter, - const bool check_guide_rate) const; + const bool check_guide_rate, + DeferredLogger& deferred_logger) const; std::pair checkGroupConstraintsProd(const Group& group, const WellGroupHelperType& wgHelper, const Scalar efficiencyFactor, const RateConvFunc& rateConverter, - const bool check_guide_rate) const; + const bool check_guide_rate, + DeferredLogger& deferred_logger) const; const WellInterfaceGeneric& well_; //!< Reference to well interface }; diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index e789dc5bdf..3e1d7efa3e 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -76,7 +76,8 @@ WellGroupHelper::checkGroupConstraintsInj(const std::string const Phase injection_phase, const Scalar efficiency_factor, const std::vector& resv_coeff, - const bool check_guide_rate) const + const bool check_guide_rate, + DeferredLogger& deferred_logger) const { // When called for a well ('name' is a well name), 'parent' // will be the name of 'group'. But if we recurse, 'name' and @@ -106,7 +107,8 @@ WellGroupHelper::checkGroupConstraintsInj(const std::string injection_phase, efficiency_factor * group.getGroupEfficiencyFactor(), resv_coeff, - check_guide_rate); + check_guide_rate, + deferred_logger); } // This can be false for FLD-controlled groups, we must therefore @@ -132,7 +134,7 @@ WellGroupHelper::checkGroupConstraintsInj(const std::string this->groupState(), injection_phase, group.has_gpmaint_control(injection_phase, current_group_control), - this->deferredLogger()}; + deferred_logger}; WGHelpers::FractionCalculator fcalc {this->schedule_, *this, @@ -208,7 +210,7 @@ WellGroupHelper::checkGroupConstraintsInj(const std::string ctrl = group.injectionControls(injection_phase, this->summary_state_); - const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. // Then ... // TODO finish explanation. @@ -250,7 +252,8 @@ WellGroupHelper::checkGroupConstraintsProd(const std::strin const Scalar* rates, const Scalar efficiency_factor, const std::vector& resv_coeff, - const bool check_guide_rate) const + const bool check_guide_rate, + DeferredLogger& deferred_logger) const { // When called for a well ('name' is a well name), 'parent' // will be the name of 'group'. But if we recurse, 'name' and @@ -279,7 +282,8 @@ WellGroupHelper::checkGroupConstraintsProd(const std::strin rates, efficiency_factor * group.getGroupEfficiencyFactor(), resv_coeff, - check_guide_rate); + check_guide_rate, + deferred_logger); } // This can be false for FLD-controlled groups, we must therefore @@ -331,7 +335,7 @@ WellGroupHelper::checkGroupConstraintsProd(const std::strin if (!group.has_gpmaint_control(current_group_control)) ctrl = group.productionControls(this->summary_state_); - const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. // Then ... // TODO finish explanation. @@ -646,7 +650,8 @@ WellGroupHelper::getWellGroupTargetInjector(const std::stri const Scalar* rates, Phase injection_phase, const Scalar efficiency_factor, - const std::vector& resv_coeff) const + const std::vector& resv_coeff, + DeferredLogger& deferred_logger) const { // This function computes a wells group target. // 'parent' will be the name of 'group'. But if we recurse, 'name' and @@ -670,7 +675,8 @@ WellGroupHelper::getWellGroupTargetInjector(const std::stri rates, injection_phase, efficiency_factor * group.getGroupEfficiencyFactor(), - resv_coeff); + resv_coeff, + deferred_logger); } // This can be false for FLD-controlled groups, we must therefore @@ -696,7 +702,7 @@ WellGroupHelper::getWellGroupTargetInjector(const std::stri this->groupState(), injection_phase, group.has_gpmaint_control(injection_phase, current_group_control), - this->deferredLogger()}; + deferred_logger}; WGHelpers::FractionCalculator fcalc {this->schedule_, *this, @@ -722,7 +728,7 @@ WellGroupHelper::getWellGroupTargetInjector(const std::stri ctrl = group.injectionControls(injection_phase, this->summary_state_); - const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. // Then ... // TODO finish explanation. @@ -777,7 +783,8 @@ WellGroupHelper::getWellGroupTargetProducer(const std::stri const Group& group, const Scalar* rates, const Scalar efficiency_factor, - const std::vector& resv_coeff) const + const std::vector& resv_coeff, + DeferredLogger& deferred_logger) const { // This function computes a wells group target. // 'parent' will be the name of 'group'. But if we recurse, 'name' and @@ -802,7 +809,8 @@ WellGroupHelper::getWellGroupTargetProducer(const std::stri parent_group, rates, efficiency_factor * group.getGroupEfficiencyFactor(), - resv_coeff); + resv_coeff, + deferred_logger); } // This can be false for FLD-controlled groups, we must therefore @@ -850,7 +858,7 @@ WellGroupHelper::getWellGroupTargetProducer(const std::stri if (!group.has_gpmaint_control(current_group_control)) ctrl = group.productionControls(this->summary_state_); - const Scalar orig_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + const Scalar orig_target = tcalc.groupTarget(ctrl, deferred_logger); // Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP. // Then ... // TODO finish explanation. @@ -1166,12 +1174,13 @@ template int WellGroupHelper::updateGroupControlledWells(const bool is_production_group, const Phase injection_phase, - GroupState& group_state) const + GroupState& group_state, + DeferredLogger& deferred_logger) const { OPM_TIMEFUNCTION(); const auto& group_name = "FIELD"; return this->updateGroupControlledWellsRecursive_( - group_name, is_production_group, injection_phase, group_state); + group_name, is_production_group, injection_phase, group_state, deferred_logger); } template @@ -1433,12 +1442,15 @@ template std::pair, Scalar> WellGroupHelper::worstOffendingWell(const Group& group, const Group::ProductionCMode& offended_control, - const Parallel::Communication& comm) const + const Parallel::Communication& comm, + DeferredLogger& deferred_logger) const { std::pair, Scalar> offending_well {std::nullopt, 0.0}; for (const std::string& child_group : group.groups()) { const auto& this_group = this->schedule_.getGroup(child_group, this->report_step_); - const auto& offending_well_this = this->worstOffendingWell(this_group, offended_control, comm); + const auto& offending_well_this = this->worstOffendingWell( + this_group, offended_control, comm, deferred_logger + ); if (offending_well_this.second > offending_well.second) { offending_well = offending_well_this; } @@ -1452,9 +1464,6 @@ WellGroupHelper::worstOffendingWell(const Group& group, const auto& pu = this->phase_usage_info_; if (well_index.has_value() && this->wellState().wellIsOwned(well_index.value(), child_well)) { const auto& ws = this->wellState().well(child_well); - // Cannot use deferredLogger() accessor here since it's non-const and this method is const. - // Direct pointer dereference is safe as the logger object itself is mutable. - auto& deferred_logger = *this->deferred_logger_; switch (offended_control) { case Group::ProductionCMode::ORAT: { const int oil_pos = pu.canonicalToActivePhaseIdx(IndexTraits::oilPhaseIdx); @@ -1718,7 +1727,8 @@ WellGroupHelper::updateGroupControlledWellsRecursive_( const std::string& group_name, const bool is_production_group, const Phase injection_phase, - GroupState& group_state) const + GroupState& group_state, + DeferredLogger& deferred_logger) const { const Group& group = this->schedule_.getGroup(group_name, this->report_step_); int num_wells = 0; @@ -1737,10 +1747,10 @@ WellGroupHelper::updateGroupControlledWellsRecursive_( if (included) { num_wells += this->updateGroupControlledWellsRecursive_( - child_group, is_production_group, injection_phase, group_state); + child_group, is_production_group, injection_phase, group_state, deferred_logger); } else { this->updateGroupControlledWellsRecursive_( - child_group, is_production_group, injection_phase, group_state); + child_group, is_production_group, injection_phase, group_state, deferred_logger); } } for (const std::string& child_well : group.wells()) { @@ -1794,7 +1804,7 @@ WellGroupHelper::updateGroupControlledWellsRecursive_( group.name(), group_state, group.has_gpmaint_control(control_group_cmode)}; - const auto& control_group_target = tcalc.groupTarget(ctrl, this->deferredLogger()); + const auto& control_group_target = tcalc.groupTarget(ctrl, deferred_logger); // Calculates the guide rate of the parent group with control. // It is allowed that the guide rate of this group is defaulted. The guide rate will be diff --git a/opm/simulators/wells/WellGroupHelper.hpp b/opm/simulators/wells/WellGroupHelper.hpp index 2ca3209d2e..331403c447 100644 --- a/opm/simulators/wells/WellGroupHelper.hpp +++ b/opm/simulators/wells/WellGroupHelper.hpp @@ -119,7 +119,8 @@ public: const Phase injection_phase, const Scalar efficiency_factor, const std::vector& resv_coeff, - const bool check_guide_rate) const; + const bool check_guide_rate, + DeferredLogger& deferred_logger) const; std::pair checkGroupConstraintsProd(const std::string& name, const std::string& parent, @@ -127,17 +128,13 @@ public: const Scalar* rates, const Scalar efficiency_factor, const std::vector& resv_coeff, - const bool check_guide_rate) const; + const bool check_guide_rate, + DeferredLogger& deferred_logger) const; std::map computeNetworkPressures(const Network::ExtNetwork& network, const VFPProdProperties& vfp_prod_props, const Parallel::Communication& comm) const; - DeferredLogger& deferredLogger() const - { - return *this->deferred_logger_; - } - Scalar getGuideRate(const std::string& name, const GuideRateModel::Target target) const; GuideRate::RateVector getProductionGroupRateVector(const std::string& group_name) const; @@ -148,14 +145,16 @@ public: const Scalar* rates, const Phase injection_phase, const Scalar efficiency_factor, - const std::vector& resv_coeff) const; + const std::vector& resv_coeff, + DeferredLogger& deferred_logger) const; Scalar getWellGroupTargetProducer(const std::string& name, const std::string& parent, const Group& group, const Scalar* rates, const Scalar efficiency_factor, - const std::vector& resv_coeff) const; + const std::vector& resv_coeff, + DeferredLogger& deferred_logger) const; GuideRate::RateVector getWellRateVector(const std::string& name) const; @@ -190,11 +189,6 @@ public: void setCmodeGroup(const Group& group, GroupState& group_state) const; - void setLogger(DeferredLogger* deferred_logger) - { - deferred_logger_ = deferred_logger; - } - template void setRegionAveragePressureCalculator(const Group& group, @@ -229,7 +223,8 @@ public: /// group_name its main usage is to detect cases where there is no wells under group control int updateGroupControlledWells(const bool is_production_group, const Phase injection_phase, - GroupState& group_state) const; + GroupState& group_state, + DeferredLogger& deferred_logger) const; void updateGroupProductionRates(const Group& group, GroupState& group_state) const; @@ -267,7 +262,8 @@ public: std::pair, Scalar> worstOffendingWell(const Group& group, const Group::ProductionCMode& offended_control, - const Parallel::Communication& comm) const; + const Parallel::Communication& comm, + DeferredLogger& deferred_logger) const; private: std::string controlGroup_(const Group& group) const; @@ -294,7 +290,8 @@ private: int updateGroupControlledWellsRecursive_(const std::string& group_name, const bool is_production_group, const Phase injection_phase, - GroupState& group_state) const; + GroupState& group_state, + DeferredLogger& deferred_logger) const; void updateGroupTargetReductionRecursive_(const Group& group, const bool is_injector, @@ -307,7 +304,6 @@ private: const SummaryState& summary_state_; const GuideRate& guide_rate_; int report_step_ {0}; - DeferredLogger* deferred_logger_ {nullptr}; // NOTE: The phase usage info seems to be read-only throughout the simulation, so it should be safe // to store a reference to it here. const PhaseUsageInfo& phase_usage_info_; diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index b5ef2d311b..6882d6b61a 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -183,7 +183,8 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, const SummaryState& summaryState, const bool check_guide_rate, - WellStateType& well_state) const + WellStateType& well_state, + DeferredLogger& deferred_logger) const { const auto& group_state = wgHelper.groupState(); @@ -205,7 +206,7 @@ checkGroupConstraints(const WellGroupHelperType& wgHelper, return WellGroupConstraints(*this).checkGroupConstraints(wgHelper, schedule, summaryState, - rCoeff, check_guide_rate, well_state); + rCoeff, check_guide_rate, well_state, deferred_logger); } template @@ -223,7 +224,7 @@ checkConstraints(const WellGroupHelperType& wgHelper, return true; } else { return checkGroupConstraints(wgHelper, schedule, - summaryState, true, well_state); + summaryState, true, well_state, deferred_logger); } } diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.hpp b/opm/simulators/wells/WellInterfaceFluidSystem.hpp index 6550d816c9..2f4654a95c 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.hpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.hpp @@ -95,7 +95,8 @@ protected: const Schedule& schedule, const SummaryState& summaryState, const bool check_guide_rate, - WellStateType& well_state) const; + WellStateType& well_state, + DeferredLogger& deferred_logger) const; bool checkConstraints(const WellGroupHelperType& wgHelper, const Schedule& schedule, diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 83dfd4d04a..f2638b8a13 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -234,7 +234,7 @@ namespace Opm changed = this->checkIndividualConstraints(ws, summaryState, deferred_logger); } else if (iog == IndividualOrGroup::Group) { changed = this->checkGroupConstraints( - wgHelper, schedule, summaryState, true, well_state + wgHelper, schedule, summaryState, true, well_state, deferred_logger ); } else { assert(iog == IndividualOrGroup::Both); @@ -320,7 +320,7 @@ namespace Opm } if (hasGroupControl && this->param_.check_group_constraints_inner_well_iterations_) { changed = changed || this->checkGroupConstraints( - wgHelper, schedule, summary_state, false, well_state + wgHelper, schedule, summary_state, false, well_state, deferred_logger ); } From bd166c1e9c9aadbcb1c3cf05b643fb4b357693f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 15:40:41 +0200 Subject: [PATCH 21/26] Fix typo --- opm/simulators/wells/BlackoilWellModelConstraints.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.hpp b/opm/simulators/wells/BlackoilWellModelConstraints.hpp index dd2de86d67..2f648d223d 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.hpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.hpp @@ -73,7 +73,7 @@ public: GroupState& group_state, DeferredLogger& deferred_logger) const; - //! \brief Update the ndividual controls for wells in a group. Return true if a group control is changed + //! \brief Update the individual controls for wells in a group. Return true if a group control is changed bool updateGroupIndividualControl(const Group& group, const int reportStepIdx, const int max_number_of_group_switch, From bf8f5e51fe1c1232915596bf935d279c36edd8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 17:31:51 +0200 Subject: [PATCH 22/26] Add comments about guard pattern --- opm/simulators/wells/StandardWell_impl.hpp | 6 ++++++ opm/simulators/wells/WellInterface_impl.hpp | 14 +++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index f587dcef6c..dc60c10f62 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -1530,6 +1530,9 @@ namespace Opm // create a copy of the well_state to use. If the operability checking is sucessful, we use this one // to replace the original one WellStateType well_state_copy = simulator.problem().wellModel().wellState(); + // Ensure that wgHelper uses well_state_copy as WellState for the rest of this function, + // and the guard ensures that the original well state is restored at scope exit, i.e. at + // the end of this function. auto guard = const_cast(wgHelper).pushWellState(well_state_copy); // Get the current controls. @@ -1630,6 +1633,9 @@ namespace Opm // store a copy of the well state, we don't want to update the real well state WellStateType well_state_copy = wgHelper.wellState(); + // Ensure that wgHelper uses well_state_copy as WellState for the rest of this function, + // and the guard ensures that the original well state is restored at scope exit, i.e. at + // the end of this function. auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index f2638b8a13..591674430e 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -398,6 +398,9 @@ namespace Opm deferred_logger.info(" well " + this->name() + " is being tested"); WellStateType well_state_copy = well_state; + // Ensure that wgHelper uses well_state_copy as WellState for the well testing + // and the guard ensures that the original well state is restored at scope exit, i.e. at + // the end of this function. auto guard = const_cast(wgHelper).pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->indexOfWell()); @@ -735,6 +738,9 @@ namespace Opm OPM_TIMEFUNCTION(); // Solve a well using single bhp-constraint (but close if not operable under this) auto group_state = GroupState(); // empty group + // Ensure that wgHelper uses the empty group state as GroupState for iterateWellEqWithSwitching() + // and the guard ensures that the original group state is restored at scope exit, i.e. at + // the end of this function. auto group_guard = const_cast(wgHelper).pushGroupState(group_state); auto inj_controls = Well::InjectionControls(0); @@ -780,6 +786,9 @@ namespace Opm this->stopWell(); auto group_state = GroupState(); // empty group + // Ensure that wgHelper uses the empty group state as GroupState for iterateWellEqWithSwitching() + // and the guard ensures that the original group state is restored at scope exit, i.e. at + // the end of this function. auto group_guard = const_cast(wgHelper).pushGroupState(group_state); auto inj_controls = Well::InjectionControls(0); @@ -1224,8 +1233,11 @@ namespace Opm this->operability_status_.resetOperability(); WellStateType well_state_copy = wgHelper.wellState(); const double dt = simulator.timeStepSize(); - // equations should be converged at this stage, so only one it is needed + // Ensure that wgHelper uses well_state_copy as WellState for iterateWellEquations() + // and the guard ensures that the original well state is restored at scope exit, i.e. at + // the end of this function. auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + // equations should be converged at this stage, so only one it is needed bool converged = iterateWellEquations(simulator, dt, wgHelper, well_state_copy, deferred_logger); return converged; } From 674953bd760ecb7c81638cc71ceff316fdd0cca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 17:46:39 +0200 Subject: [PATCH 23/26] Make method const --- opm/simulators/wells/BlackoilWellModelConstraints.cpp | 2 +- opm/simulators/wells/BlackoilWellModelConstraints.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index be1b8bb470..25fb60de4c 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -495,7 +495,7 @@ updateGroupIndividualControl(const Group& group, std::map>& closed_offending_wells, GroupState& group_state, WellState& well_state, - DeferredLogger& deferred_logger) + DeferredLogger& deferred_logger) const { bool changed = false; if (group.isInjectionGroup()) diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.hpp b/opm/simulators/wells/BlackoilWellModelConstraints.hpp index 2f648d223d..d9b5a3a862 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.hpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.hpp @@ -83,7 +83,7 @@ public: std::map>& closed_offending_wells, GroupState& group_state, WellState& well_state, - DeferredLogger& deferred_logger); + DeferredLogger& deferred_logger) const; private: //! \brief Check and return value and type of constraints for an injection well group. From c06cc249a556644b5d499eb8a8c0773818b04e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Fri, 24 Oct 2025 17:47:33 +0200 Subject: [PATCH 24/26] Remove space --- opm/simulators/wells/WellGroupHelper.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/opm/simulators/wells/WellGroupHelper.cpp b/opm/simulators/wells/WellGroupHelper.cpp index 3e1d7efa3e..e79ffe0526 100644 --- a/opm/simulators/wells/WellGroupHelper.cpp +++ b/opm/simulators/wells/WellGroupHelper.cpp @@ -39,9 +39,7 @@ WellGroupHelper::WellGroupHelper(WellState& phase_usage_info - - ) + const PhaseUsageInfo& phase_usage_info) : well_state_ {&well_state} , group_state_ {&group_state} , schedule_ {schedule} From f0abef921f0a4b1fd23d7749f94a1ba9f3cd0c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Mon, 27 Oct 2025 11:28:45 +0100 Subject: [PATCH 25/26] Use maybe_unused instead of static_cast() --- opm/simulators/wells/StandardWell_impl.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index dc60c10f62..c31d470b92 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -2334,7 +2334,7 @@ namespace Opm std::optional::Scalar> StandardWell:: computeBhpAtThpLimitInj(const Simulator& simulator, - const WellGroupHelperType& wgHelper, + [[maybe_unused]] const WellGroupHelperType& wgHelper, const SummaryState& summary_state, DeferredLogger& deferred_logger) const { @@ -2342,7 +2342,6 @@ namespace Opm // with MultisegmentWell::computeBhpAtThpLimitInj which uses it in fratesIter lambda. // This maintains parallel API structure between well types and allows for future // enhancements without breaking the interface. - static_cast(wgHelper); // Make the frates() function. auto frates = [this, &simulator, &deferred_logger](const Scalar bhp) { From dd1c844e16b3d111b3972be3172727a705731166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Tue, 28 Oct 2025 12:51:19 +0100 Subject: [PATCH 26/26] Get rid of const_cast by using a non-const copy Get rid of const_cast on WellGroupHelper object by using a non-const copy instead. --- .../wells/MultisegmentWell_impl.hpp | 16 +++++----- opm/simulators/wells/StandardWell_impl.hpp | 18 ++++++----- opm/simulators/wells/WellInterface_impl.hpp | 30 +++++++++++-------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/opm/simulators/wells/MultisegmentWell_impl.hpp b/opm/simulators/wells/MultisegmentWell_impl.hpp index 3851417cde..fdcc286879 100644 --- a/opm/simulators/wells/MultisegmentWell_impl.hpp +++ b/opm/simulators/wells/MultisegmentWell_impl.hpp @@ -418,9 +418,10 @@ namespace Opm well_copy.debug_cost_counter_ = 0; + WellGroupHelperType wgHelper_copy = wgHelper; // store a copy of the well state, we don't want to update the real well state - WellStateType well_state_copy = simulator.problem().wellModel().wellState(); - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + WellStateType well_state_copy = wgHelper_copy.wellState(); + auto guard = wgHelper_copy.pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); // Get the current controls. @@ -462,7 +463,7 @@ namespace Opm well_copy.calculateExplicitQuantities(simulator, well_state_copy, deferred_logger); const double dt = simulator.timeStepSize(); // iterate to get a solution at the given bhp. - well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper, + well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state_copy, deferred_logger); // compute the potential and store in the flux vector. @@ -543,9 +544,10 @@ namespace Opm MultisegmentWell well_copy(*this); well_copy.debug_cost_counter_ = 0; + WellGroupHelperType wgHelper_copy = wgHelper; // store a copy of the well state, we don't want to update the real well state - WellStateType well_state_copy = wgHelper.wellState(); - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + WellStateType well_state_copy = wgHelper_copy.wellState(); + auto guard = wgHelper_copy.pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); // get current controls @@ -584,11 +586,11 @@ namespace Opm bool converged = false; if (this->well_ecl_.isProducer()) { converged = well_copy.solveWellWithOperabilityCheck( - simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state_copy, deferred_logger ); } else { converged = well_copy.iterateWellEqWithSwitching( - simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state_copy, deferred_logger ); } diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index c31d470b92..ff0b5d4a68 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -1529,11 +1529,12 @@ namespace Opm // iterate to get a more accurate well density // create a copy of the well_state to use. If the operability checking is sucessful, we use this one // to replace the original one - WellStateType well_state_copy = simulator.problem().wellModel().wellState(); - // Ensure that wgHelper uses well_state_copy as WellState for the rest of this function, + WellGroupHelperType wgHelper_copy = wgHelper; + WellStateType well_state_copy = wgHelper_copy.wellState(); + // Ensure that wgHelper_copy uses well_state_copy as WellState for the rest of this function, // and the guard ensures that the original well state is restored at scope exit, i.e. at // the end of this function. - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + auto guard = wgHelper_copy.pushWellState(well_state_copy); // Get the current controls. const auto& summary_state = simulator.vanguard().summaryState(); @@ -1567,7 +1568,7 @@ namespace Opm const double dt = simulator.timeStepSize(); const bool converged = well_copy.iterateWellEqWithControl( - simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state_copy, deferred_logger ); if (!converged) { const std::string msg = " well " + name() + " did not get converged during well potential calculations " @@ -1633,10 +1634,11 @@ namespace Opm // store a copy of the well state, we don't want to update the real well state WellStateType well_state_copy = wgHelper.wellState(); - // Ensure that wgHelper uses well_state_copy as WellState for the rest of this function, + WellGroupHelperType wgHelper_copy = wgHelper; + // Ensure that wgHelper_copy uses well_state_copy as WellState for the rest of this function, // and the guard ensures that the original well state is restored at scope exit, i.e. at // the end of this function. - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + auto guard = wgHelper_copy.pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->index_of_well_); // get current controls @@ -1675,11 +1677,11 @@ namespace Opm bool converged = false; if (this->well_ecl_.isProducer()) { converged = well_copy.solveWellWithOperabilityCheck( - simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state_copy, deferred_logger ); } else { converged = well_copy.iterateWellEqWithSwitching( - simulator, dt, inj_controls, prod_controls, wgHelper, well_state_copy, deferred_logger + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state_copy, deferred_logger ); } diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 591674430e..f61e178a10 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -397,11 +397,12 @@ namespace Opm const auto& group_state = wgHelper.groupState(); deferred_logger.info(" well " + this->name() + " is being tested"); + WellGroupHelperType wgHelper_copy = wgHelper; WellStateType well_state_copy = well_state; // Ensure that wgHelper uses well_state_copy as WellState for the well testing // and the guard ensures that the original well state is restored at scope exit, i.e. at // the end of this function. - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + auto guard = wgHelper_copy.pushWellState(well_state_copy); auto& ws = well_state_copy.well(this->indexOfWell()); const auto& summary_state = simulator.vanguard().summaryState(); @@ -439,7 +440,7 @@ namespace Opm // untill the number of closed completions do not increase anymore. while (testWell) { const std::size_t original_number_closed_completions = welltest_state_temp.num_closed_completions(); - bool converged = solveWellForTesting(simulator, wgHelper, well_state_copy, deferred_logger); + bool converged = solveWellForTesting(simulator, wgHelper_copy, well_state_copy, deferred_logger); if (!converged) { const auto msg = fmt::format("WTEST: Well {} is not solvable (physical)", this->name()); deferred_logger.debug(msg); @@ -447,7 +448,7 @@ namespace Opm } - updateWellOperability(simulator, well_state_copy, wgHelper, deferred_logger); + updateWellOperability(simulator, well_state_copy, wgHelper_copy, deferred_logger); if ( !this->isOperableAndSolvable() ) { const auto msg = fmt::format("WTEST: Well {} is not operable (physical)", this->name()); deferred_logger.debug(msg); @@ -455,7 +456,7 @@ namespace Opm } std::vector potentials; try { - computeWellPotentials(simulator, well_state_copy, wgHelper, potentials, deferred_logger); + computeWellPotentials(simulator, well_state_copy, wgHelper_copy, potentials, deferred_logger); } catch (const std::exception& e) { const std::string msg = fmt::format("well {}: computeWellPotentials() " "failed during testing for re-opening: ", @@ -738,10 +739,11 @@ namespace Opm OPM_TIMEFUNCTION(); // Solve a well using single bhp-constraint (but close if not operable under this) auto group_state = GroupState(); // empty group - // Ensure that wgHelper uses the empty group state as GroupState for iterateWellEqWithSwitching() + WellGroupHelperType wgHelper_copy = wgHelper; + // Ensure that wgHelper_copy uses the empty group state as GroupState for iterateWellEqWithSwitching() // and the guard ensures that the original group state is restored at scope exit, i.e. at // the end of this function. - auto group_guard = const_cast(wgHelper).pushGroupState(group_state); + auto group_guard = wgHelper_copy.pushGroupState(group_state); auto inj_controls = Well::InjectionControls(0); auto prod_controls = Well::ProductionControls(0); @@ -763,7 +765,7 @@ namespace Opm ws.bhp = bhp; // solve const bool converged = this->iterateWellEqWithSwitching( - simulator, dt, inj_controls, prod_controls, wgHelper, + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state, deferred_logger, /*fixed_control*/true ); ws.injection_cmode = cmode_inj; @@ -786,15 +788,16 @@ namespace Opm this->stopWell(); auto group_state = GroupState(); // empty group - // Ensure that wgHelper uses the empty group state as GroupState for iterateWellEqWithSwitching() + WellGroupHelperType wgHelper_copy = wgHelper; + // Ensure that wgHelper_copy uses the empty group state as GroupState for iterateWellEqWithSwitching() // and the guard ensures that the original group state is restored at scope exit, i.e. at // the end of this function. - auto group_guard = const_cast(wgHelper).pushGroupState(group_state); + auto group_guard = wgHelper_copy.pushGroupState(group_state); auto inj_controls = Well::InjectionControls(0); auto prod_controls = Well::ProductionControls(0); const bool converged = this->iterateWellEqWithSwitching( - simulator, dt, inj_controls, prod_controls, wgHelper, well_state, + simulator, dt, inj_controls, prod_controls, wgHelper_copy, well_state, deferred_logger, /*fixed_control*/true, /*fixed_status*/ true ); this->wellStatus_ = well_status_orig; @@ -1231,14 +1234,15 @@ namespace Opm // only makes sense if we're using this parameter is true assert(this->param_.local_well_solver_control_switching_); this->operability_status_.resetOperability(); - WellStateType well_state_copy = wgHelper.wellState(); + WellGroupHelperType wgHelper_copy = wgHelper; + WellStateType well_state_copy = wgHelper_copy.wellState(); const double dt = simulator.timeStepSize(); // Ensure that wgHelper uses well_state_copy as WellState for iterateWellEquations() // and the guard ensures that the original well state is restored at scope exit, i.e. at // the end of this function. - auto guard = const_cast(wgHelper).pushWellState(well_state_copy); + auto guard = wgHelper_copy.pushWellState(well_state_copy); // equations should be converged at this stage, so only one it is needed - bool converged = iterateWellEquations(simulator, dt, wgHelper, well_state_copy, deferred_logger); + bool converged = iterateWellEquations(simulator, dt, wgHelper_copy, well_state_copy, deferred_logger); return converged; }