From 85e95fb87d11c458b90231bbd7ef4a8988c09247 Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Mon, 4 May 2020 15:56:34 +0200 Subject: [PATCH] Implement min/max for GCONSALE Only default action RATE is implemented for violation of maximum sales target --- opm/simulators/wells/BlackoilWellModel.hpp | 3 +- .../wells/BlackoilWellModel_impl.hpp | 99 ++++++++++++++++--- opm/simulators/wells/TargetCalculator.hpp | 12 ++- opm/simulators/wells/WellGroupHelpers.cpp | 17 ++-- opm/simulators/wells/WellInterface_impl.hpp | 8 +- .../wells/WellStateFullyImplicitBlackoil.hpp | 19 ++++ 6 files changed, 132 insertions(+), 26 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModel.hpp b/opm/simulators/wells/BlackoilWellModel.hpp index 81f681a97..b5e56002d 100644 --- a/opm/simulators/wells/BlackoilWellModel.hpp +++ b/opm/simulators/wells/BlackoilWellModel.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -428,7 +429,7 @@ namespace Opm { bool checkGroupConstraints(const Group& group, Opm::DeferredLogger& deferred_logger) const; Group::ProductionCMode checkGroupProductionConstraints(const Group& group, Opm::DeferredLogger& deferred_logger) const; Group::InjectionCMode checkGroupInjectionConstraints(const Group& group, const Phase& phase) const; - void checkGconsaleLimits(const Group& group) const; + void checkGconsaleLimits(const Group& group, WellState& well_state, Opm::DeferredLogger& deferred_logger ) const; void updateGroupHigherControls(Opm::DeferredLogger& deferred_logger, std::set& switched_groups); void checkGroupHigherConstraints(const Group& group, Opm::DeferredLogger& deferred_logger, std::set& switched_groups); diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index bb56ca838..f87c1a14d 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -464,16 +464,17 @@ namespace Opm { const std::string msg = "A zero well potential is returned for output purposes. "; local_deferredLogger.warning("WELL_POTENTIAL_CALCULATION_FAILED", msg); } + + // check group sales limits at the end of the timestep + const Group& fieldGroup = schedule().getGroup("FIELD", reportStepIdx); + checkGconsaleLimits(fieldGroup, well_state_, local_deferredLogger); + previous_well_state_ = well_state_; Opm::DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger); if (terminal_output_) { global_deferredLogger.logMessages(); } - - // check group sales limits at the end of the timestep - const Group& fieldGroup = schedule().getGroup("FIELD", reportStepIdx); - checkGconsaleLimits(fieldGroup); } @@ -2046,12 +2047,12 @@ namespace Opm { template void BlackoilWellModel:: - checkGconsaleLimits(const Group& group) const + checkGconsaleLimits(const Group& group, WellState& well_state, Opm::DeferredLogger& deferred_logger) const { const int reportStepIdx = ebosSimulator_.episodeIndex(); // call recursively down the group hiearchy for (const std::string& groupName : group.groups()) { - checkGconsaleLimits( schedule().getGroup(groupName, reportStepIdx)); + checkGconsaleLimits( schedule().getGroup(groupName, reportStepIdx), well_state, deferred_logger); } // only for groups with gas injection controls @@ -2063,20 +2064,26 @@ namespace Opm { if (!schedule().gConSale(reportStepIdx).has(group.name())) return; + std::ostringstream ss; + const auto& summaryState = ebosSimulator_.vanguard().summaryState(); const auto& comm = ebosSimulator_.vanguard().grid().comm(); - const auto& well_state = well_state_; - const auto& controls = group.injectionControls(Phase::GAS, summaryState); + const auto& inj_controls = group.injectionControls(Phase::GAS, summaryState); const auto& gconsale = schedule().gConSale(reportStepIdx).get(group.name(), summaryState); + const Group::ProductionCMode& oldProductionControl = well_state.currentProductionGroupControl(group.name()); + - double sales_rate = 0.0; int gasPos = phase_usage_.phase_pos[BlackoilPhases::Vapour]; - sales_rate += WellGroupHelpers::sumWellRates(group, schedule(), well_state, reportStepIdx, gasPos, /*isInjector*/false); - sales_rate -= WellGroupHelpers::sumWellRates(group, schedule(), well_state, reportStepIdx, gasPos, /*isInjector*/true); + double production_rate = WellGroupHelpers::sumWellRates(group, schedule(), well_state, reportStepIdx, gasPos, /*isInjector*/false); + double injection_rate = WellGroupHelpers::sumWellRates(group, schedule(), well_state, reportStepIdx, gasPos, /*isInjector*/true); // sum over all nodes - sales_rate = comm.sum(sales_rate); + injection_rate = comm.sum(injection_rate); + production_rate = comm.sum(production_rate); + + double sales_rate = production_rate - injection_rate; + double production_target = gconsale.sales_target + injection_rate; // add import rate and substract consumption rate for group for gas if (schedule().gConSump(reportStepIdx).has(group.name())) { @@ -2084,18 +2091,80 @@ namespace Opm { if (phase_usage_.phase_used[BlackoilPhases::Vapour]) { sales_rate += gconsump.import_rate; sales_rate -= gconsump.consumption_rate; + production_target -= gconsump.import_rate; + production_target += gconsump.consumption_rate; } } if (sales_rate > gconsale.max_sales_rate) { - OPM_THROW(std::runtime_error, "Group " + group.name() + " has sale rate more then the maximum permitted value. Not implemented in Flow" ); + + switch(gconsale.max_proc) { + case GConSale::MaxProcedure::NONE: { + if (oldProductionControl != Group::ProductionCMode::GRAT && oldProductionControl != Group::ProductionCMode::NONE) { + ss << "Group sales exceed maximum limit, but the action is NONE for " + group.name() + ". Nothing happens"; + } + break; + } + case GConSale::MaxProcedure::CON: { + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GCONSALE exceed limit CON not implemented", deferred_logger); + break; + } + case GConSale::MaxProcedure::CON_P: { + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GCONSALE exceed limit CON_P not implemented", deferred_logger); + break; + } + case GConSale::MaxProcedure::WELL: { + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GCONSALE exceed limit WELL not implemented", deferred_logger); + break; + } + case GConSale::MaxProcedure::PLUG: { + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GCONSALE exceed limit PLUG not implemented", deferred_logger); + break; + } + case GConSale::MaxProcedure::MAXR: { + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GCONSALE exceed limit MAXR not implemented", deferred_logger); + break; + } + case GConSale::MaxProcedure::END: { + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GCONSALE exceed limit END not implemented", deferred_logger); + break; + } + case GConSale::MaxProcedure::RATE: { + well_state.setCurrentProductionGroupControl(group.name(), Group::ProductionCMode::GRAT); + ss << "Maximum GCONSALE limit violated for " << group.name() << ". The group is switched from "; + ss << Group::ProductionCMode2String(oldProductionControl) << " to " << Group::ProductionCMode2String(Group::ProductionCMode::GRAT); + ss << " and limited by the maximum sales rate after consumption and import are considered" ; + well_state.setCurrentGroupGratTargetFromSales(group.name(), production_target); + break; + } + default: + throw("Invalid procedure for maximum rate limit selected for group" + group.name()); + } } if (sales_rate < gconsale.min_sales_rate) { - OPM_THROW(std::runtime_error, "Group " + group.name() + " has sale rate less then minimum permitted value. Not implemented in Flow" ); + const Group::ProductionCMode& currentProductionControl = well_state.currentProductionGroupControl(group.name()); + if ( currentProductionControl == Group::ProductionCMode::GRAT ) { + ss << "Group " + group.name() + " has sale rate less then minimum permitted value and is under GRAT control. \n"; + ss << "The GRAT is increased to meet the sales minimum rate. \n"; + well_state.setCurrentGroupGratTargetFromSales(group.name(), production_target); + //} else if () {//TODO add action for WGASPROD + //} else if () {//TODO add action for drilling queue + } else { + ss << "Group " + group.name() + " has sale rate less then minimum permitted value but cannot increase the group production rate \n"; + ss << "or adjust gas production using WGASPROD or drill new wells to meet the sales target. \n"; + ss << "Note that WGASPROD and drilling queues are not implemented in Flow. No action is taken. \n "; + } } if (gconsale.sales_target < 0.0) { - OPM_THROW(std::runtime_error, "Group " + group.name() + " has sale rate target less then zero. Not implemented in Flow" ); + OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + " has sale rate target less then zero. Not implemented in Flow" , deferred_logger); } + auto cc = Dune::MPIHelper::getCollectiveCommunication(); + if (cc.size() > 1) { + ss << " on rank " << cc.rank(); + } + if (!ss.str().empty()) + deferred_logger.info(ss.str()); + } diff --git a/opm/simulators/wells/TargetCalculator.hpp b/opm/simulators/wells/TargetCalculator.hpp index dc2cf2f5d..903e2aba5 100644 --- a/opm/simulators/wells/TargetCalculator.hpp +++ b/opm/simulators/wells/TargetCalculator.hpp @@ -46,11 +46,14 @@ namespace WellGroupHelpers public: TargetCalculator(const Group::ProductionCMode cmode, const PhaseUsage& pu, - const std::vector& resv_coeff) + const std::vector& resv_coeff, + const double group_grat_target_from_sales) : cmode_(cmode) , pu_(pu) , resv_coeff_(resv_coeff) + , group_grat_target_from_sales_(group_grat_target_from_sales) { + } template @@ -107,7 +110,13 @@ namespace WellGroupHelpers case Group::ProductionCMode::WRAT: return ctrl.water_target; case Group::ProductionCMode::GRAT: + { + // gas target may have been adjusted by GCONSALE + if ( group_grat_target_from_sales_ > 0) + return group_grat_target_from_sales_; + return ctrl.gas_target; + } case Group::ProductionCMode::LRAT: return ctrl.liquid_target; case Group::ProductionCMode::RESV: @@ -151,6 +160,7 @@ namespace WellGroupHelpers Group::ProductionCMode cmode_; const PhaseUsage& pu_; const std::vector& resv_coeff_; + const double group_grat_target_from_sales_; }; } // namespace WellGroupHelpers diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index 39a36d031..19fff64b2 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -1008,14 +1008,8 @@ namespace WellGroupHelpers assert(phasePos == pu.phase_pos[BlackoilPhases::Vapour]); // Gas injection rate = Total gas production rate + gas import rate - gas consumption rate - sales rate; + // Gas import and consumption is already included in the REIN rates double inj_rate = wellState.currentInjectionREINRates(group.name())[phasePos]; - if (schedule.gConSump(reportStepIdx).has(group.name())) { - const auto& gconsump = schedule.gConSump(reportStepIdx).get(group.name(), summaryState); - if (pu.phase_used[BlackoilPhases::Vapour]) { - inj_rate += gconsump.import_rate; - inj_rate -= gconsump.consumption_rate; - } - } const auto& gconsale = schedule.gConSale(reportStepIdx).get(group.name(), summaryState); inj_rate -= gconsale.sales_target; @@ -1125,7 +1119,14 @@ namespace WellGroupHelpers // 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. - TargetCalculator tcalc(currentGroupControl, pu, resv_coeff); + + // gconsale may adjust the grat target. + // the adjusted rates is send to the targetCalculator + double gratTargetFromSales = 0.0; + if (wellState.hasGroupGratTargetFromSales(group.name())) + gratTargetFromSales = wellState.currentGroupGratTargetFromSales(group.name()); + + TargetCalculator tcalc(currentGroupControl, pu, resv_coeff, gratTargetFromSales); FractionCalculator fcalc(schedule, wellState, reportStepIdx, guideRate, tcalc.guideTargetMode(), pu); auto localFraction = [&](const std::string& child) { return fcalc.localFraction(child, name); }; diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index afdd1be97..c035f76d9 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -2246,7 +2246,13 @@ namespace Opm std::vector resv_coeff(phaseUsage().num_phases, 1.0); rateConverter_.calcCoeff(0, pvtRegionIdx_, resv_coeff); // FIPNUM region 0 here, should use FIPNUM from WELSPECS. - WellGroupHelpers::TargetCalculator tcalc(currentGroupControl, pu, resv_coeff); + // gconsale may adjust the grat target. + // the adjusted rates is send to the targetCalculator + double gratTargetFromSales = 0.0; + if (well_state.hasGroupGratTargetFromSales(group.name())) + gratTargetFromSales = well_state.currentGroupGratTargetFromSales(group.name()); + + WellGroupHelpers::TargetCalculator tcalc(currentGroupControl, pu, resv_coeff, gratTargetFromSales); WellGroupHelpers::FractionCalculator fcalc(schedule, well_state, current_step_, guide_rate_, tcalc.guideTargetMode(), pu); auto localFraction = [&](const std::string& child) { diff --git a/opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp b/opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp index 9e9ceadec..3b19d0545 100644 --- a/opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp +++ b/opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp @@ -447,6 +447,24 @@ namespace Opm return it->second; } + void setCurrentGroupGratTargetFromSales(const std::string& groupName, const double& target ) { + group_grat_target_from_sales[groupName] = target; + } + + bool hasGroupGratTargetFromSales(const std::string& groupName) const { + auto it = group_grat_target_from_sales.find(groupName); + return it != group_grat_target_from_sales.end(); + } + + const double& currentGroupGratTargetFromSales(const std::string& groupName) const { + auto it = group_grat_target_from_sales.find(groupName); + + if (it == group_grat_target_from_sales.end()) + OPM_THROW(std::logic_error, "Could not find any grat target from sales for group " << groupName); + + return it->second; + } + void setCurrentGroupInjectionPotentials(const std::string& groupName, const std::vector& pot ) { injection_group_potentials[groupName] = pot; } @@ -1042,6 +1060,7 @@ namespace Opm std::map> injection_group_potentials; std::map injection_group_vrep_rates; std::map> injection_group_rein_rates; + std::map group_grat_target_from_sales; std::vector perfRateSolvent_;