Merge pull request #5172 from totto82/gconprod7

Implement WELL as group limit action
This commit is contained in:
Bård Skaflestad 2024-04-11 17:36:55 +02:00 committed by GitHub
commit b1be391d4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 211 additions and 29 deletions

View File

@ -97,7 +97,7 @@ partiallySupported()
"GCONPROD",
{
{2,{true, allow_values<std::string> {"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<std::string> {"NONE", "RATE"}, "GCONPROD(ACTION): Only NONE and RATE are supported"}},
{7,{true, allow_values<std::string> {"NONE", "RATE", "WELL"}, "GCONPROD(ACTION): Only NONE and RATE are supported"}},
{11,{true, allow_values<std::string> {"NONE", "RATE"}, "GCONPROD(ACTWAT): Only NONE and RATE are supported"}}, // WATER_EXCEED_PROCEDURE
{12,{true, allow_values<std::string> {"NONE", "RATE"}, "GCONPROD(ACTGAS): Only NONE and RATE are supported"}}, // GAS_EXCEED_PROCEDURE
{13,{true, allow_values<std::string> {"NONE", "RATE"}, "GCONPROD(ACTLIQ): Only NONE and RATE are supported"}}, // LIQUID_EXCEED_PROCEDURE
@ -330,7 +330,8 @@ partiallySupported()
{
"WTEST",
{
{3,{true, allow_values<std::string> {"E", "P", "EP", "PE", ""}, "WTEST(TEST): only the E (economic) and P (physical) reason is currently supported"}}, // REASON
{3,{true, allow_values<std::string> {"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
},
},
{

View File

@ -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<std::string>& 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::worstOffendingWell(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::pair<std::string,Opm::Phase>,std::string>& switched_inj,
std::map<std::string, std::string>& switched_prod,
std::map<std::string, std::pair<std::string, std::string>>& 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<std::string> 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));
}
}
}

View File

@ -24,7 +24,7 @@
#define OPM_BLACKOILWELLMODEL_CONSTRAINTS_HEADER_INCLUDED
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <optional>
#include <utility>
namespace Opm {
@ -59,18 +59,22 @@ public:
GroupState& group_state,
DeferredLogger& deferred_logger) const;
//! \brief Execute action on broken constraint for a production well group.
void actionOnBrokenConstraints(const Group& group,
//! \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<std::string>& worst_offending_well,
GroupState& group_state,
DeferredLogger& deferred_logger) const;
//! \brief Update the individual controls for wells in a group.
//! \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,
std::map<std::pair<std::string,Opm::Phase>,std::string>& switched_inj,
std::map<std::string, std::string>& switched_prod,
std::map<std::string, std::pair<std::string, std::string>>& closed_offending_wells,
GroupState& group_state,
WellState& well_state,
DeferredLogger& deferred_logger) const;

View File

@ -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<std::string> 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,

View File

@ -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<class Serializer>
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:
@ -582,8 +585,12 @@ protected:
bool wellStructureChangedDynamically_{false};
// Store maps of group name and new group controls for output
std::map<std::string, std::string> switched_prod_groups_;
std::map<std::pair<std::string, Opm::Phase>, std::string> switched_inj_groups_;
// Store map of group name and close offending well for output
std::map<std::string, std::pair<std::string, std::string>> closed_offending_wells_;
private:
WellInterfaceGeneric* getGenWell(const std::string& well_name);

View File

@ -28,7 +28,7 @@
#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
#include <opm/simulators/wells/BlackoilWellModelConstraints.hpp>
#include <opm/simulators/wells/ParallelPAvgDynamicSourceData.hpp>
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
@ -44,6 +44,7 @@
#include <cassert>
#include <iomanip>
#include <utility>
#include <optional>
#include <fmt/format.h>
@ -1079,6 +1080,7 @@ namespace Opm {
last_report_ = SimulatorReportSingle();
Dune::Timer perfTimer;
perfTimer.start();
closed_offending_wells_.clear();
{
const int episodeIdx = simulator_.episodeIndex();
@ -2132,15 +2134,18 @@ namespace Opm {
changed = true;
updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger);
}
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);
@ -2172,9 +2177,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();
}
}

View File

@ -1400,6 +1400,94 @@ namespace WellGroupHelpers
return std::make_pair(current_rate > target_rate, scale);
}
template <class Comm>
std::pair<std::optional<std::string>, double> worstOffendingWell( 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<std::optional<std::string>, 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 = worstOffendingWell(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 violating_rate = 0.0;
double prefered_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:
violating_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]];
break;
case Group::ProductionCMode::GRAT:
violating_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Vapour]];
break;
case Group::ProductionCMode::WRAT:
violating_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]];
break;
case Group::ProductionCMode::LRAT:
assert(pu.phase_used[BlackoilPhases::Liquid]);
assert(pu.phase_used[BlackoilPhases::Aqua]);
violating_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]] + ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]];
break;
case Group::ProductionCMode::RESV:
for (int p = 0; p < pu.num_phases; ++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:
prefered_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]];
break;
case Phase::GAS:
prefered_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Vapour]];
break;
case Phase::WATER:
prefered_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]];
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);
double 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 <class AverageRegionalPressureType>
void setRegionAveragePressureCalculator(const Group& group,
const Schedule& schedule,
@ -1576,6 +1664,16 @@ namespace WellGroupHelpers
const PhaseUsage&,
AvgPMap&);
template
std::pair<std::optional<std::string>, double> worstOffendingWell<Parallel::Communication>( 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

View File

@ -179,6 +179,19 @@ namespace WellGroupHelpers
GroupState& group_state,
bool sum_rank);
/// Returns the name of the worst offending well and its fraction (i.e. violated_phase / preferred_phase)
template <class Comm>
std::pair<std::optional<std::string>, double> worstOffendingWell(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 <class RegionalValues>
void updateGpMaintTargetForGroups(const Group& group,
const Schedule& schedule,

View File

@ -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<double>&, std::vector<double>&) override