diff --git a/flowexperimental/flowexp.hpp b/flowexperimental/flowexp.hpp index 9f996c7e67..25d25a4c06 100644 --- a/flowexperimental/flowexp.hpp +++ b/flowexperimental/flowexp.hpp @@ -169,6 +169,7 @@ public: Parameters::Hide(); Parameters::Hide(); Parameters::Hide(); + Parameters::Hide(); Parameters::Hide>(); Parameters::Hide(); Parameters::Hide(); diff --git a/opm/simulators/flow/BlackoilModelParameters.cpp b/opm/simulators/flow/BlackoilModelParameters.cpp index 2006849dd6..62aabb0edf 100644 --- a/opm/simulators/flow/BlackoilModelParameters.cpp +++ b/opm/simulators/flow/BlackoilModelParameters.cpp @@ -62,7 +62,8 @@ BlackoilModelParameters::BlackoilModelParameters() max_niter_inner_well_iter_ = Parameters::Get(); shut_unsolvable_wells_ = Parameters::Get(); max_inner_iter_wells_ = Parameters::Get(); - max_well_status_switch_ = Parameters::Get(); + max_well_status_switch_inner_iter_ = Parameters::Get(); + max_well_status_switch_ = Parameters::Get(); maxSinglePrecisionTimeStep_ = Parameters::Get>() * 24 * 60 * 60; min_strict_cnv_iter_ = Parameters::Get(); min_strict_mb_iter_ = Parameters::Get(); @@ -185,7 +186,9 @@ void BlackoilModelParameters::registerParameters() Parameters::Register ("Maximum number of inner iterations for standard wells"); Parameters::Register - ("Maximum number of status switching (shut<->open) in inner iterations for wells"); + ("Maximum number of status switching (stop<->open) for a well during inner iterations."); + Parameters::Register + ("Maximum number of status switching (stop<->open) for a well during a time-step."); Parameters::Register ("Use alternative well rate initialization procedure"); Parameters::Register> diff --git a/opm/simulators/flow/BlackoilModelParameters.hpp b/opm/simulators/flow/BlackoilModelParameters.hpp index 228f5e5285..a5fc8025ab 100644 --- a/opm/simulators/flow/BlackoilModelParameters.hpp +++ b/opm/simulators/flow/BlackoilModelParameters.hpp @@ -104,6 +104,7 @@ struct MaxNewtonIterationsWithInnerWellIterations { static constexpr int value = struct MaxInnerIterMsWells { static constexpr int value = 100; }; struct MaxInnerIterWells { static constexpr int value = 50; }; struct MaxWellStatusSwitchInInnerIterWells { static constexpr int value = 99; }; +struct MaxWellStatusSwitchForWells { static constexpr int value = 99; }; struct ShutUnsolvableWells { static constexpr bool value = true; }; struct AlternativeWellRateInit { static constexpr bool value = true; }; struct StrictOuterIterWells { static constexpr int value = 6; }; @@ -324,7 +325,10 @@ public: /// Maximum number of iterations in the well/group switch algorithm int well_group_constraints_max_iterations_; - /// Maximum number of status switches (open<->shut> in local well iterations + /// Maximum number of status switches (open<->stop> in local well iterations + int max_well_status_switch_inner_iter_; + + /// Maximum number of status switches (open<->stop> during a time step int max_well_status_switch_; /// Nonlinear solver type: newton or nldd. diff --git a/opm/simulators/wells/MultisegmentWell_impl.hpp b/opm/simulators/wells/MultisegmentWell_impl.hpp index e30802c175..09a3a396f6 100644 --- a/opm/simulators/wells/MultisegmentWell_impl.hpp +++ b/opm/simulators/wells/MultisegmentWell_impl.hpp @@ -1634,7 +1634,7 @@ namespace Opm // that unless this number is >1, we may get stuck in a cycle const int min_its_after_switch = 3; // We also want to restrict the number of status switches to avoid oscillation between STOP<->OPEN - const int max_status_switch = this->param_.max_well_status_switch_; + const int max_status_switch = this->param_.max_well_status_switch_inner_iter_; int its_since_last_switch = min_its_after_switch; int switch_count= 0; int status_switch_count = 0; diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index ad68cac682..5af1382add 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -2386,7 +2386,7 @@ namespace Opm // that unless this number is >1, we may get stuck in a cycle constexpr int min_its_after_switch = 4; // We also want to restrict the number of status switches to avoid oscillation between STOP<->OPEN - const int max_status_switch = this->param_.max_well_status_switch_; + const int max_status_switch = this->param_.max_well_status_switch_inner_iter_; int its_since_last_switch = min_its_after_switch; int switch_count= 0; // if we fail to solve eqs, we reset status/operability before leaving diff --git a/opm/simulators/wells/WellInterface.hpp b/opm/simulators/wells/WellInterface.hpp index 9ac1463fe4..381a0628a8 100644 --- a/opm/simulators/wells/WellInterface.hpp +++ b/opm/simulators/wells/WellInterface.hpp @@ -180,7 +180,6 @@ public: const GroupState& group_state, DeferredLogger& deferred_logger); - virtual void computeWellRatesWithBhp(const Simulator& ebosSimulator, const Scalar& bhp, std::vector& well_flux, @@ -368,6 +367,7 @@ protected: std::vector B_avg_; bool changed_to_stopped_this_step_ = false; bool thp_update_iterations = false; + int number_of_well_reopenings_{0}; Scalar wpolymer() const; Scalar wfoam() const; diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index d9917bdbdb..613ab248ef 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -574,7 +574,6 @@ namespace Opm { OPM_TIMEFUNCTION(); const auto& summary_state = simulator.vanguard().summaryState(); - bool is_operable = true; bool converged = true; auto& ws = well_state.well(this->index_of_well_); // if well is stopped, check if we can reopen @@ -585,10 +584,13 @@ namespace Opm // no intersection with ipr const auto msg = fmt::format("estimateOperableBhp: Did not find operable BHP for well {}", this->name()); deferred_logger.debug(msg); - is_operable = false; + // well can't operate using explicit fractions stop the well // solve with zero rates - solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); + converged = solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); this->stopWell(); + this->operability_status_.can_obtain_bhp_with_thp_limit = false; + this->operability_status_.obey_thp_limit_under_bhp_limit = false; + return converged; } else { // solve well with the estimated target bhp (or limit) ws.thp = this->getTHPConstraint(summary_state); @@ -598,9 +600,8 @@ namespace Opm } } // solve well-equation - if (is_operable) { - converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); - } + converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); + const bool isThp = ws.production_cmode == Well::ProducerCMode::THP; // check stability of solution under thp-control @@ -633,11 +634,13 @@ namespace Opm this->openWell(); auto bhp_target = estimateOperableBhp(simulator, dt, well_state, summary_state, deferred_logger); if (!bhp_target.has_value()) { - // well can't operate using explicit fractions - is_operable = false; // solve with zero rate + // well can't operate using explicit fractions stop the well converged = solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); this->stopWell(); + this->operability_status_.can_obtain_bhp_with_thp_limit = false; + this->operability_status_.obey_thp_limit_under_bhp_limit = false; + return converged; } else { // solve well with the estimated target bhp (or limit) const Scalar bhp = std::max(bhp_target.value(), @@ -655,9 +658,8 @@ namespace Opm } } // update operability - is_operable = is_operable && !this->wellIsStopped(); - this->operability_status_.can_obtain_bhp_with_thp_limit = is_operable; - this->operability_status_.obey_thp_limit_under_bhp_limit = is_operable; + this->operability_status_.can_obtain_bhp_with_thp_limit = !this->wellIsStopped(); + this->operability_status_.obey_thp_limit_under_bhp_limit = !this->wellIsStopped(); return converged; } @@ -897,6 +899,25 @@ namespace Opm [](Scalar rate) { return rate != Scalar(0.0); }); this->operability_status_.solvable = true; + if (number_of_well_reopenings_ >= this->param_.max_well_status_switch_) { + // only output the first time + if (number_of_well_reopenings_ == this->param_.max_well_status_switch_) { + const std::string msg = fmt::format("well {} is oscillating between open and stop. \n" + "We don't allow for more than {} re-openings " + "and the well is therefore kept stopped.", + this->name(), number_of_well_reopenings_); + deferred_logger.debug(msg); + } + this->stopWell(); + changed_to_stopped_this_step_ = true; + bool converged_zero_rate = this->solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); + if (this->param_.shut_unsolvable_wells_ && !converged_zero_rate ) { + this->operability_status_.solvable = false; + } + // we increse the number of reopenings to avoid output in the next iteration + number_of_well_reopenings_++; + return; + } bool converged = this->iterateWellEquations(simulator, dt, well_state, group_state, deferred_logger); if (converged) { @@ -908,6 +929,7 @@ namespace Opm this->operability_status_.resetOperability(); this->openWell(); deferred_logger.debug(" " + this->name() + " is re-opened after being stopped during local solve"); + number_of_well_reopenings_++; } } else { // unsolvable wells are treated as not operable and will not be solved for in this iteration.