diff --git a/opm/simulators/flow/BlackoilModelParametersEbos.hpp b/opm/simulators/flow/BlackoilModelParametersEbos.hpp index 1a4864cfa..0f3fc81ea 100644 --- a/opm/simulators/flow/BlackoilModelParametersEbos.hpp +++ b/opm/simulators/flow/BlackoilModelParametersEbos.hpp @@ -142,6 +142,10 @@ struct MaxNewtonIterationsWithInnerWellIterations { using type = UndefinedProperty; }; template +struct ShutUnsolvableWells { + using type = UndefinedProperty; +}; +template struct MaxInnerIterWells { using type = UndefinedProperty; }; @@ -251,6 +255,10 @@ struct MaxInnerIterWells { static constexpr int value = 50; }; template +struct ShutUnsolvableWells { + static constexpr bool value = false; +}; +template struct AlternativeWellRateInit { static constexpr bool value = true; }; @@ -341,6 +349,9 @@ namespace Opm /// Maximum newton iterations with inner well iterations int max_niter_inner_well_iter_; + /// Whether to shut unsolvable well + bool shut_unsolvable_wells_; + /// Maximum inner iteration number for standard wells int max_inner_iter_wells_; @@ -398,6 +409,7 @@ namespace Opm strict_inner_iter_ms_wells_ = EWOMS_GET_PARAM(TypeTag, int, StrictInnerIterMsWells); regularization_factor_ms_wells_ = EWOMS_GET_PARAM(TypeTag, Scalar, RegularizationFactorMsw); max_niter_inner_well_iter_ = EWOMS_GET_PARAM(TypeTag, int, MaxNewtonIterationsWithInnerWellIterations); + shut_unsolvable_wells_ = EWOMS_GET_PARAM(TypeTag, bool, ShutUnsolvableWells); max_inner_iter_wells_ = EWOMS_GET_PARAM(TypeTag, int, MaxInnerIterWells); maxSinglePrecisionTimeStep_ = EWOMS_GET_PARAM(TypeTag, Scalar, MaxSinglePrecisionDays) *24*60*60; max_strict_iter_ = EWOMS_GET_PARAM(TypeTag, int, MaxStrictIter); @@ -430,6 +442,7 @@ namespace Opm EWOMS_REGISTER_PARAM(TypeTag, int, MaxInnerIterMsWells, "Maximum number of inner iterations for multi-segment wells"); EWOMS_REGISTER_PARAM(TypeTag, int, StrictInnerIterMsWells, "Number of inner iterations for multi-segment wells with strict tolerance"); EWOMS_REGISTER_PARAM(TypeTag, int, MaxNewtonIterationsWithInnerWellIterations, "Maximum newton iterations with inner well iterations"); + EWOMS_REGISTER_PARAM(TypeTag, bool, ShutUnsolvableWells, "Shut unsolvable wells"); EWOMS_REGISTER_PARAM(TypeTag, int, MaxInnerIterWells, "Maximum number of inner iterations for standard wells"); EWOMS_REGISTER_PARAM(TypeTag, bool, AlternativeWellRateInit, "Use alternative well rate initialization procedure"); EWOMS_REGISTER_PARAM(TypeTag, Scalar, RegularizationFactorMsw, "Regularization factor for ms wells"); diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 61afa1281..636a5a3f0 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -1262,6 +1262,7 @@ namespace Opm { std::string exc_msg; try { for (const auto& well : well_container_) { + const bool old_well_operable = well->isOperable(); well->checkWellOperability(ebosSimulator_, this->wellState(), deferred_logger); if (!well->isOperable() ) continue; @@ -1279,6 +1280,22 @@ namespace Opm { well->solveWellEquation(ebosSimulator_, this->wellState(), this->groupState(), deferred_logger); } + const bool well_operable = well->isOperable(); + if (!well_operable && old_well_operable) { + const Well& well_ecl = getWellEcl(well->name()); + if (well_ecl.getAutomaticShutIn()) { + deferred_logger.info(" well " + well->name() + " gets SHUT at the beginning of the time step "); + } else { + if (!well->wellIsStopped()) { + deferred_logger.info(" well " + well->name() + " gets STOPPED at the beginning of the time step "); + well->stopWell(); + } + } + } else if (well_operable && !old_well_operable) { + deferred_logger.info(" well " + well->name() + " gets REVIVED at the beginning of the time step "); + well->openWell(); + } + } // end of for (const auto& well : well_container_) updatePrimaryVariables(deferred_logger); } catch (const std::runtime_error& e) { diff --git a/opm/simulators/wells/WellInterface.hpp b/opm/simulators/wells/WellInterface.hpp index 05229a599..87b6f2165 100644 --- a/opm/simulators/wells/WellInterface.hpp +++ b/opm/simulators/wells/WellInterface.hpp @@ -340,6 +340,8 @@ protected: void solveWellForTesting(const Simulator& ebosSimulator, WellState& well_state, const GroupState& group_state, DeferredLogger& deferred_logger); + + bool shutUnsolvableWells() const; }; } diff --git a/opm/simulators/wells/WellInterfaceGeneric.hpp b/opm/simulators/wells/WellInterfaceGeneric.hpp index b7b1dcc55..f034f4843 100644 --- a/opm/simulators/wells/WellInterfaceGeneric.hpp +++ b/opm/simulators/wells/WellInterfaceGeneric.hpp @@ -177,7 +177,7 @@ protected: // definition of the struct OperabilityStatus struct OperabilityStatus { bool isOperable() const { - if (!operable_under_only_bhp_limit) { + if (!operable_under_only_bhp_limit || !solvable) { return false; } else { return ( (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()) ); @@ -197,6 +197,7 @@ protected: obey_thp_limit_under_bhp_limit = true; can_obtain_bhp_with_thp_limit = true; obey_bhp_limit_with_thp_limit = true; + solvable = true; } // whether the well can be operated under bhp limit @@ -210,6 +211,8 @@ protected: bool can_obtain_bhp_with_thp_limit = true; // whether the well obey bhp limit when operated under thp limit bool obey_bhp_limit_with_thp_limit = true; + // the well is solveable + bool solvable = true; }; OperabilityStatus operability_status_; diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 62eee1513..e1d8e7a09 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -342,12 +342,14 @@ namespace Opm const WellState well_state0 = well_state; const double dt = ebosSimulator.timeStepSize(); const bool converged = iterateWellEquations(ebosSimulator, dt, well_state, group_state, deferred_logger); - if (converged) { - deferred_logger.debug("Compute initial well solution for well " + this->name() + ". Converged"); - } else { + if (!converged) { const int max_iter = param_.max_welleq_iter_; deferred_logger.debug("Compute initial well solution for well " + this->name() + ". Failed to converge in " + std::to_string(max_iter) + " iterations"); + // the well operability system currently works only for producers in prediction mode + if (this->shutUnsolvableWells()) + this->operability_status_.solvable = false; + well_state = well_state0; } } @@ -363,13 +365,35 @@ namespace Opm const GroupState& group_state, DeferredLogger& deferred_logger) { - + const bool old_well_operable = this->operability_status_.isOperable(); checkWellOperability(ebosSimulator, well_state, deferred_logger); // only use inner well iterations for the first newton iterations. const int iteration_idx = ebosSimulator.model().newtonMethod().numIterations(); - if (iteration_idx < param_.max_niter_inner_well_iter_) { - this->iterateWellEquations(ebosSimulator, dt, well_state, group_state, deferred_logger); + bool converged = true; + if (iteration_idx < param_.max_niter_inner_well_iter_) + converged = this->iterateWellEquations(ebosSimulator, dt, well_state, group_state, deferred_logger); + + // unsolvable wells are treated as not operable and will not be solved for in this iteration. + if (!converged) { + if (this->shutUnsolvableWells()) + this->operability_status_.solvable = false; + } + const bool well_operable = this->operability_status_.isOperable(); + if (!well_operable && old_well_operable) { + if (this->well_ecl_.getAutomaticShutIn()) { + deferred_logger.info(" well " + this->name() + " gets SHUT during iteration "); + } else { + if (!this->wellIsStopped()) { + deferred_logger.info(" well " + this->name() + " gets STOPPED during iteration "); + this->stopWell(); + changed_to_stopped_this_step_ = true; + } + } + } else if (well_operable && !old_well_operable) { + deferred_logger.info(" well " + this->name() + " gets REVIVED during iteration "); + this->openWell(); + changed_to_stopped_this_step_ = false; } const auto& summary_state = ebosSimulator.vanguard().summaryState(); @@ -498,27 +522,18 @@ namespace Opm return; } - const bool old_well_operable = this->operability_status_.isOperable(); - updateWellOperability(ebos_simulator, well_state, deferred_logger); + } - const bool well_operable = this->operability_status_.isOperable(); - if (!well_operable && old_well_operable) { - if (this->well_ecl_.getAutomaticShutIn()) { - deferred_logger.info(" well " + this->name() + " gets SHUT during iteration "); - } else { - if (!this->wellIsStopped()) { - deferred_logger.info(" well " + this->name() + " gets STOPPED during iteration "); - this->stopWell(); - changed_to_stopped_this_step_ = true; - } - } - } else if (well_operable && !old_well_operable) { - deferred_logger.info(" well " + this->name() + " gets REVIVED during iteration "); - this->openWell(); - changed_to_stopped_this_step_ = false; - } + template + bool + WellInterface:: + shutUnsolvableWells() const + { + bool shut_unsolvable_wells = param_.shut_unsolvable_wells_; + // the well operability system currently works only for producers in prediction mode + return shut_unsolvable_wells && !this->isInjector() && this->underPredictionMode(); }