diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 5593a49f63..fbcea68517 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -1947,6 +1947,82 @@ operator==(const BlackoilWellModelGeneric& rhs) const && this->gen_gaslift_ == rhs.gen_gaslift_; } +template +void +BlackoilWellModelGeneric:: +updateNONEProductionGroups(DeferredLogger& deferred_logger) +{ + auto& group_state = this->groupState(); + const auto& prod_group_controls = group_state.get_production_controls(); + if (prod_group_controls.empty()) { + return; + } + + const auto& well_state = this->wellState(); + // numbers of the group production controls + // one group can have multiple controls, including NONE + const std::size_t size_pc = prod_group_controls.size(); + // Collect groups that currently provide production targets to any well on this rank + std::unordered_set targeted_production_groups; // TODO: better name + targeted_production_groups.reserve(size_pc); + + for (const auto& wname : well_state.wells()) { + const auto& ws = well_state.well(wname); + if (ws.producer && ws.production_cmode == WellProducerCMode::GRUP) { + const auto& group_target = ws.group_target; + if (group_target.has_value()) { + targeted_production_groups.insert(group_target->first); + } + } + } + + // parallel communication to synchronize production groups used on all processes + std::vector gnames; + gnames.reserve(size_pc); + for (const auto& gprod : prod_group_controls) { + gnames.push_back(gprod.first); + } + + std::vector local_used(size_pc, 0); + for (std::size_t i = 0; i < size_pc; ++i) { + if (targeted_production_groups.find(gnames[i]) != targeted_production_groups.end()) { + local_used[i] = 1; + } + } + + std::vector global_used(size_pc, 0); + + // with the following code, we only need to communicate once + if (comm_.size() > 1 && size_pc > 0) { + std::vector gathered(comm_.size() * size_pc, 0); + comm_.allgather(local_used.data(), static_cast(size_pc), gathered.data()); + + for (int r = 0; r < comm_.size(); ++r) { + const std::size_t base = static_cast(r) * size_pc; + for (std::size_t i = 0; i < size_pc; ++i) { + global_used[i] = global_used[i] || gathered[base + i]; + } + } + } else { + global_used = local_used; + } + + for (std::size_t i = 0; i < size_pc; ++i) { + if (global_used[i] > 0) { + continue; + } + const auto& gname = gnames[i]; + if (group_state.production_control(gname) != Group::ProductionCMode::NONE) { + if (comm_.rank() == 0) { + const std::string msg = fmt::format("Production group {} has no constraints active, setting control mode to NONE", gname); + deferred_logger.info(msg); + } + group_state.production_control(gname, Group::ProductionCMode::NONE); + } + } +} + + template class BlackoilWellModelGeneric; #if FLOW_INSTANTIATE_FLOAT diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index c800292a28..90e6d3b77c 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -508,6 +508,8 @@ protected: void assignMassGasRate(data::Wells& wsrpt, const Scalar gasDensity) const; + void updateNONEProductionGroups(DeferredLogger& deferred_logger); + Schedule& schedule_; const SummaryState& summaryState_; diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 45874dbd86..d5d084fb95 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -770,6 +770,8 @@ namespace Opm { this->calculateProductivityIndexValues(local_deferredLogger); + this->updateNONEProductionGroups(local_deferredLogger); + this->commitWGState(); //reporting output temperatures diff --git a/opm/simulators/wells/GroupState.cpp b/opm/simulators/wells/GroupState.cpp index 7288bcf9e4..36ed2b689d 100644 --- a/opm/simulators/wells/GroupState.cpp +++ b/opm/simulators/wells/GroupState.cpp @@ -412,6 +412,13 @@ GroupState::production_control(const std::string& gname) const return group_iter->second; } +template +const std::map& +GroupState::get_production_controls() const +{ + return this->production_controls; +}; + //------------------------------------------------------------------------- template diff --git a/opm/simulators/wells/GroupState.hpp b/opm/simulators/wells/GroupState.hpp index 046a900ed0..7fed1a4cf6 100644 --- a/opm/simulators/wells/GroupState.hpp +++ b/opm/simulators/wells/GroupState.hpp @@ -99,6 +99,7 @@ public: bool has_production_control(const std::string& gname) const; void production_control(const std::string& gname, Group::ProductionCMode cmode); Group::ProductionCMode production_control(const std::string& gname) const; + const std::map& get_production_controls() const; bool has_injection_control(const std::string& gname, Phase phase) const; void injection_control(const std::string& gname, Phase phase, Group::InjectionCMode cmode); diff --git a/opm/simulators/wells/GroupStateHelper.cpp b/opm/simulators/wells/GroupStateHelper.cpp index 12675f1d9c..266b5b5fc9 100644 --- a/opm/simulators/wells/GroupStateHelper.cpp +++ b/opm/simulators/wells/GroupStateHelper.cpp @@ -553,7 +553,7 @@ getGroupRatesAvailableForHigherLevelControl(const Group& group, const bool is_in } template -std::optional +std::optional> GroupStateHelper::getWellGroupTargetInjector(const std::string& name, const std::string& parent, const Group& group, @@ -644,7 +644,7 @@ GroupStateHelper::getWellGroupTargetInjector(const std::str do_addback); // Avoid negative target rates coming from too large local reductions. - return std::max(Scalar(0.0), target / efficiency_factor); + return std::make_pair(group.name(), std::max(Scalar(0.0), target / efficiency_factor)); } template @@ -660,7 +660,7 @@ GroupStateHelper::getWellGroupTargetProducer(const std::str // '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 + // Efficiency factor 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(); diff --git a/opm/simulators/wells/GroupStateHelper.hpp b/opm/simulators/wells/GroupStateHelper.hpp index b36af8371e..cc17610643 100644 --- a/opm/simulators/wells/GroupStateHelper.hpp +++ b/opm/simulators/wells/GroupStateHelper.hpp @@ -243,20 +243,20 @@ public: GuideRate::RateVector getProductionGroupRateVector(const std::string& group_name) const; - std::optional 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; + std::optional> 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; std::optional> 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::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;