diff --git a/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp b/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp index 844a01c63..4f05e63f2 100644 --- a/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp +++ b/opm/simulators/utils/PartiallySupportedFlowKeywords.cpp @@ -97,7 +97,7 @@ partiallySupported() "GCONPROD", { {2,{true, allow_values {"NONE", "FLD", "ORAT", "WRAT", "GRAT", "LRAT", "RESV"}, "GCONPROD(TARGET): valid option should be NONE/FLD/ORAT/WRAT/GRAT/LRAT or RESV"}}, // CONTROL_MODE - {7,{true, allow_values {"NONE", "RATE"}, "GCONPROD(ACTION): Only NONE and RATE are supported"}}, + {7,{true, allow_values {"NONE", "RATE", "WELL"}, "GCONPROD(ACTION): Only NONE and RATE are supported"}}, {11,{true, allow_values {"NONE", "RATE"}, "GCONPROD(ACTWAT): Only NONE and RATE are supported"}}, // WATER_EXCEED_PROCEDURE {12,{true, allow_values {"NONE", "RATE"}, "GCONPROD(ACTGAS): Only NONE and RATE are supported"}}, // GAS_EXCEED_PROCEDURE {13,{true, allow_values {"NONE", "RATE"}, "GCONPROD(ACTLIQ): Only NONE and RATE are supported"}}, // LIQUID_EXCEED_PROCEDURE @@ -330,7 +330,8 @@ partiallySupported() { "WTEST", { - {3,{true, allow_values {"E", "P", "EP", "PE", ""}, "WTEST(TEST): only the E (economic) and P (physical) reason is currently supported"}}, // REASON + {3,{true, allow_values {"E", "P", "G", "EP", "PE", "EG", "GE", "PG", "GP", "PEG", "PGE", "EPG", "EGP", "GEP", "GPE"}, + "WTEST(TEST): only the E (economic) and P (physical) and G (group) reason is currently supported"}}, // REASON }, }, { diff --git a/opm/simulators/wells/BlackoilWellModelConstraints.cpp b/opm/simulators/wells/BlackoilWellModelConstraints.cpp index 991f790c6..0503914fe 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.cpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.cpp @@ -400,13 +400,18 @@ actionOnBrokenConstraints(const Group& group, } } -void BlackoilWellModelConstraints:: +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 { + + bool changed = false; const Group::ProductionCMode oldControl = wellModel_.groupState().production_control(group.name()); std::string ss; @@ -421,6 +426,8 @@ actionOnBrokenConstraints(const Group& group, group.name(), Group::ProductionCMode2String(oldControl), Group::ProductionCMode2String(newControl)); + + changed = true; } else { ss = fmt::format("Procedure on exceeding {} limit is NONE for group {}. Nothing is done.", @@ -439,7 +446,9 @@ actionOnBrokenConstraints(const Group& group, break; } case Group::ExceedAction::WELL: { - OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GroupProductionExceedLimit WELL not implemented", deferred_logger); + + std::tie(worst_offending_well, std::ignore) = WellGroupHelpers::shutWorstOffendingWell(group, wellModel_.schedule(), reportStepIdx, + newControl, wellModel_.phaseUsage(), wellModel_.comm(), well_state, deferred_logger); break; } case Group::ExceedAction::PLUG: { @@ -454,6 +463,7 @@ actionOnBrokenConstraints(const Group& group, Group::ProductionCMode2String(oldControl), Group::ProductionCMode2String(newControl)); } + changed = true; break; } default: @@ -462,6 +472,8 @@ actionOnBrokenConstraints(const Group& group, if (!ss.empty() && wellModel_.comm().rank() == 0) deferred_logger.debug(ss); + + return changed; } bool BlackoilWellModelConstraints:: @@ -469,6 +481,7 @@ updateGroupIndividualControl(const Group& group, const int reportStepIdx, std::map,std::string>& switched_inj, std::map& switched_prod, + std::map>& closed_offending_wells, GroupState& group_state, WellState& well_state, DeferredLogger& deferred_logger) const @@ -506,23 +519,30 @@ updateGroupIndividualControl(const Group& group, reportStepIdx, deferred_logger); const auto controls = group.productionControls(wellModel_.summaryState()); + if (changed_this.first != Group::ProductionCMode::NONE) { - switched_prod.insert_or_assign(group.name(), - Group::ProductionCMode2String(changed_this.first)); - - this->actionOnBrokenConstraints(group, + std::optional worst_offending_well = std::nullopt; + changed = this->actionOnBrokenConstraints(group, reportStepIdx, controls.group_limit_action, - changed_this.first, + changed_this.first, well_state, + worst_offending_well, group_state, deferred_logger); - WellGroupHelpers::updateWellRatesFromGroupTargetScale(changed_this.second, - group, - wellModel_.schedule(), - reportStepIdx, - /* isInjector */ false, - wellModel_.groupState(), - well_state); - changed = true; + + if(changed) { + switched_prod.insert_or_assign(group.name(), + Group::ProductionCMode2String(changed_this.first)); + WellGroupHelpers::updateWellRatesFromGroupTargetScale(changed_this.second, + group, + wellModel_.schedule(), + reportStepIdx, + /* isInjector */ false, + wellModel_.groupState(), + 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 ced16501d..525695ea9 100644 --- a/opm/simulators/wells/BlackoilWellModelConstraints.hpp +++ b/opm/simulators/wells/BlackoilWellModelConstraints.hpp @@ -24,7 +24,7 @@ #define OPM_BLACKOILWELLMODEL_CONSTRAINTS_HEADER_INCLUDED #include - +#include #include namespace Opm { @@ -60,9 +60,12 @@ public: DeferredLogger& deferred_logger) const; //! \brief Execute action on broken constraint for a production well group. - void actionOnBrokenConstraints(const Group& group, + 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; @@ -71,6 +74,7 @@ public: const int reportStepIdx, std::map,std::string>& switched_inj, std::map& switched_prod, + std::map>& closed_offending_wells, GroupState& group_state, WellState& well_state, DeferredLogger& deferred_logger) const; diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 8858d3d6a..09cd4b1fa 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -653,15 +653,20 @@ checkGroupHigherConstraints(const Group& group, resv_coeff, deferred_logger); if (is_changed) { - switched_prod_groups_.insert_or_assign(group.name(), Group::ProductionCMode2String(Group::ProductionCMode::FLD)); const auto group_limit_action = group.productionControls(summaryState_).group_limit_action; - BlackoilWellModelConstraints(*this). - actionOnBrokenConstraints(group, group_limit_action, + std::optional worst_offending_well = std::nullopt; + changed = BlackoilWellModelConstraints(*this). + actionOnBrokenConstraints(group, reportStepIdx, group_limit_action, Group::ProductionCMode::FLD, + this->wellState(), + worst_offending_well, this->groupState(), deferred_logger); - WellGroupHelpers::updateWellRatesFromGroupTargetScale(scaling_factor, group, schedule(), reportStepIdx, /* isInjector */ false, this->groupState(), this->wellState()); - changed = true; + + if (changed) { + switched_prod_groups_.insert_or_assign(group.name(), Group::ProductionCMode2String(Group::ProductionCMode::FLD)); + WellGroupHelpers::updateWellRatesFromGroupTargetScale(scaling_factor, group, schedule(), reportStepIdx, /* isInjector */ false, this->groupState(), this->wellState()); + } } } } @@ -772,10 +777,18 @@ bool BlackoilWellModelGeneric:: wasDynamicallyShutThisTimeStep(const int well_index) const { - return this->closed_this_step_.find(this->wells_ecl_[well_index].name()) != + return wasDynamicallyShutThisTimeStep(this->wells_ecl_[well_index].name()); +} + +bool +BlackoilWellModelGeneric:: +wasDynamicallyShutThisTimeStep(const std::string well_name) const +{ + return this->closed_this_step_.find(well_name) != this->closed_this_step_.end(); } + void BlackoilWellModelGeneric:: updateWsolvent(const Group& group, @@ -1028,7 +1041,7 @@ updateAndCommunicateGroupData(const int reportStepIdx, bool BlackoilWellModelGeneric:: -hasTHPConstraints() const +hasTHPConstraints() { return BlackoilWellModelConstraints(*this).hasTHPConstraints(); } diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index eb01e8451..4002bfb2f 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -179,7 +179,7 @@ public: data::GroupAndNetworkValues groupAndNetworkData(const int reportStepIdx) const; /// Return true if any well has a THP constraint. - bool hasTHPConstraints() const; + bool hasTHPConstraints(); /// Checks if network is active (at least one network well on prediction). void updateNetworkActiveState(const int report_step); @@ -213,6 +213,7 @@ public: void updateClosedWellsThisStep(const std::string& well_name) const { this->closed_this_step_.insert(well_name); } + bool wasDynamicallyShutThisTimeStep(const std::string well_name) const; template void serializeOp(Serializer& serializer) @@ -231,6 +232,7 @@ public: serializer(last_glift_opt_time_); serializer(switched_prod_groups_); serializer(switched_inj_groups_); + serializer(closed_offending_wells_); } bool operator==(const BlackoilWellModelGeneric& rhs) const @@ -247,7 +249,8 @@ public: this->nupcol_wgstate_ == rhs.nupcol_wgstate_ && this->last_glift_opt_time_ == rhs.last_glift_opt_time_ && this->switched_prod_groups_ == rhs.switched_prod_groups_ && - this->switched_inj_groups_ == rhs.switched_inj_groups_; + this->switched_inj_groups_ == rhs.switched_inj_groups_ && + this->closed_offending_wells_ == rhs.closed_offending_wells_; } protected: @@ -584,6 +587,8 @@ protected: std::map switched_prod_groups_; std::map, std::string> switched_inj_groups_; + std::map> closed_offending_wells_; + private: WellInterfaceGeneric* getGenWell(const std::string& well_name); diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 8cab72fdd..88e2212a6 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -28,7 +28,7 @@ #include #include #include - +#include #include #include #include @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -1076,6 +1077,7 @@ namespace Opm { last_report_ = SimulatorReportSingle(); Dune::Timer perfTimer; perfTimer.start(); + closed_offending_wells_.clear(); { const int episodeIdx = simulator_.episodeIndex(); @@ -2129,19 +2131,24 @@ namespace Opm { changed = true; updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger); } + + //if (iterationIdx > 1) { bool changed_individual = BlackoilWellModelConstraints(*this). updateGroupIndividualControl(group, reportStepIdx, this->switched_inj_groups_, this->switched_prod_groups_, + this->closed_offending_wells_, this->groupState(), this->wellState(), deferred_logger); + if (changed_individual) { changed = true; updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger); } + //} // call recursively down the group hierarchy for (const std::string& groupName : group.groups()) { bool changed_this = updateGroupControls( schedule().getGroup(groupName, reportStepIdx), deferred_logger, reportStepIdx,iterationIdx); @@ -2169,9 +2176,29 @@ namespace Opm { const Opm::Parallel::Communication comm = grid().comm(); DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm); + + for (const auto& [group_name, to] : closed_offending_wells_) { + if (!this->wasDynamicallyShutThisTimeStep(to.second)) { + wellTestState.close_well( + to.second, WellTestConfig::Reason::GROUP, simulationTime); + this->updateClosedWellsThisStep(to.second); + std::string msg = fmt::format("Procedure on exceeding {} limit is WELL for group {}. Well {} is {}.", + to.first, + group_name, + to.second, + "shut"); + global_deferredLogger.info(msg); + } + } + if (terminal_output_) { global_deferredLogger.logMessages(); } + + + + + } diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index aa5e9b566..d86b22632 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -1400,6 +1400,72 @@ namespace WellGroupHelpers return std::make_pair(current_rate > target_rate, scale); } + template + std::pair, double> shutWorstOffendingWell(const Group& group, + const Schedule& schedule, + const int reportStepIdx, + const Group::ProductionCMode& offendedControl, + const PhaseUsage& pu, + const Comm& comm, + const WellState& wellState, + DeferredLogger& deferred_logger) + { + std::pair, double> 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 = shutWorstOffendingWell(this_group, schedule, reportStepIdx, offendedControl, pu, 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); + double rate = 0.0; + 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: + rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]]; + break; + case Group::ProductionCMode::GRAT: + rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Vapour]]; + break; + case Group::ProductionCMode::WRAT: + rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; + break; + case Group::ProductionCMode::LRAT: + rate = -(ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]] + ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]); + break; + case Group::ProductionCMode::RESV: + rate = -(ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Liquid]] + + ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Vapour]] + + ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Aqua]]); + 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; + + } + } + rate = comm.sum(rate); + if ( rate > offending_well.second) { + offending_well = {child_well, rate}; + } + } + return offending_well; + }; + + template void setRegionAveragePressureCalculator(const Group& group, const Schedule& schedule, @@ -1576,6 +1642,16 @@ namespace WellGroupHelpers const PhaseUsage&, AvgPMap&); +template +std::pair, double> shutWorstOffendingWell( const Group& group, + const Schedule& schedule, + const int reportStepIdx, + const Group::ProductionCMode& offendedControl, + const PhaseUsage& pu, + const Parallel::Communication& comm, + const WellState& wellState, + DeferredLogger& deferred_logger); + } // namespace WellGroupHelpers } // namespace Opm diff --git a/opm/simulators/wells/WellGroupHelpers.hpp b/opm/simulators/wells/WellGroupHelpers.hpp index 957ab3b88..9f85f12b9 100644 --- a/opm/simulators/wells/WellGroupHelpers.hpp +++ b/opm/simulators/wells/WellGroupHelpers.hpp @@ -179,6 +179,18 @@ namespace WellGroupHelpers GroupState& group_state, bool sum_rank); + + template + std::pair, double> shutWorstOffendingWell( const Group& group, + const Schedule& schedule, + const int reportStepIdx, + const Group::ProductionCMode& offendedControl, + const PhaseUsage& pu, + const Comm& comm, + const WellState& wellState, + DeferredLogger& deferred_logger); + + template void updateGpMaintTargetForGroups(const Group& group, const Schedule& schedule, diff --git a/tests/test_RestartSerialization.cpp b/tests/test_RestartSerialization.cpp index 852935ac4..ff93a9230 100644 --- a/tests/test_RestartSerialization.cpp +++ b/tests/test_RestartSerialization.cpp @@ -289,6 +289,7 @@ public: last_glift_opt_time_ = 5.0; switched_prod_groups_ = {{"test4", "test5"}}; switched_inj_groups_ = {{{"test4", Phase::SOLVENT}, "test5"}}; + closed_offending_wells_ = {{"test4", {"test5", "test6"}}}; } void calcRates(const int, const int, const std::vector&, std::vector&) override