diff --git a/flowexperimental/flowexp.hpp b/flowexperimental/flowexp.hpp index a295ccf5ac..9f996c7e67 100644 --- a/flowexperimental/flowexp.hpp +++ b/flowexperimental/flowexp.hpp @@ -168,6 +168,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 4cc95c3474..2006849dd6 100644 --- a/opm/simulators/flow/BlackoilModelParameters.cpp +++ b/opm/simulators/flow/BlackoilModelParameters.cpp @@ -62,6 +62,7 @@ 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(); maxSinglePrecisionTimeStep_ = Parameters::Get>() * 24 * 60 * 60; min_strict_cnv_iter_ = Parameters::Get(); min_strict_mb_iter_ = Parameters::Get(); @@ -183,6 +184,8 @@ void BlackoilModelParameters::registerParameters() ("Shut unsolvable wells"); Parameters::Register ("Maximum number of inner iterations for standard wells"); + Parameters::Register + ("Maximum number of status switching (shut<->open) in inner iterations for wells"); 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 6847c5d1b8..228f5e5285 100644 --- a/opm/simulators/flow/BlackoilModelParameters.hpp +++ b/opm/simulators/flow/BlackoilModelParameters.hpp @@ -103,6 +103,7 @@ struct MaxPressureChangeMsWells { static constexpr Scalar value = 10*1e5; }; struct MaxNewtonIterationsWithInnerWellIterations { static constexpr int value = 8; }; struct MaxInnerIterMsWells { static constexpr int value = 100; }; struct MaxInnerIterWells { static constexpr int value = 50; }; +struct MaxWellStatusSwitchInInnerIterWells { 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; }; @@ -323,6 +324,9 @@ 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 + int max_well_status_switch_; + /// Nonlinear solver type: newton or nldd. std::string nonlinear_solver_; /// 'jacobi' and 'gauss-seidel' supported. diff --git a/opm/simulators/wells/MultisegmentWell_impl.hpp b/opm/simulators/wells/MultisegmentWell_impl.hpp index f5eba02396..e30802c175 100644 --- a/opm/simulators/wells/MultisegmentWell_impl.hpp +++ b/opm/simulators/wells/MultisegmentWell_impl.hpp @@ -1633,11 +1633,15 @@ namespace Opm // The optimal number here is subject to further investigation, but it has been observerved // 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_; int its_since_last_switch = min_its_after_switch; int switch_count= 0; + int status_switch_count = 0; // if we fail to solve eqs, we reset status/operability before leaving const auto well_status_orig = this->wellStatus_; const auto operability_orig = this->operability_status_; + auto well_status_cur = well_status_orig; // don't allow opening wells that has a stopped well status const bool allow_open = well_state.well(this->index_of_well_).status == WellStatus::OPEN; // don't allow switcing for wells under zero rate target or requested fixed status and control @@ -1650,7 +1654,7 @@ namespace Opm for (; it < max_iter_number; ++it, ++debug_cost_counter_) { ++its_since_last_switch; - if (allow_switching && its_since_last_switch >= min_its_after_switch){ + if (allow_switching && its_since_last_switch >= min_its_after_switch && status_switch_count < max_status_switch){ const Scalar wqTotal = this->primary_variables_.getWQTotal().value(); bool changed = this->updateWellControlAndStatusLocalIteration(simulator, well_state, group_state, inj_controls, prod_controls, wqTotal, @@ -1659,12 +1663,19 @@ namespace Opm if (changed) { its_since_last_switch = 0; ++switch_count; + if (well_status_cur != this->wellStatus_) { + well_status_cur = this->wellStatus_; + status_switch_count++; + } } if (!changed && final_check) { break; } else { final_check = false; } + if (status_switch_count == max_status_switch) { + this->wellStatus_ = well_status_orig; + } } if (it > this->param_.strict_inner_iter_wells_) { @@ -1750,7 +1761,7 @@ namespace Opm this->wellStatus_ = well_status_orig; this->operability_status_ = operability_orig; const std::string message = fmt::format(" Well {} did not converge in {} inner iterations (" - "{} control/status switches).", this->name(), it, switch_count); + "{} switches, {} status changes).", this->name(), it, switch_count, status_switch_count); deferred_logger.debug(message); this->primary_variables_.outputLowLimitPressureSegments(deferred_logger); } diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index 91ff54389e..ad68cac682 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -2385,6 +2385,8 @@ namespace Opm // The optimal number here is subject to further investigation, but it has been observerved // 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_; 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 @@ -2406,7 +2408,7 @@ namespace Opm this->operability_status_.solvable = true; do { its_since_last_switch++; - if (allow_switching && its_since_last_switch >= min_its_after_switch){ + if (allow_switching && its_since_last_switch >= min_its_after_switch && status_switch_count < max_status_switch){ const Scalar wqTotal = this->primary_variables_.eval(WQTotal).value(); changed = this->updateWellControlAndStatusLocalIteration(simulator, well_state, group_state, inj_controls, prod_controls, wqTotal, @@ -2424,6 +2426,9 @@ namespace Opm } else { final_check = false; } + if (status_switch_count == max_status_switch) { + this->wellStatus_ = well_status_orig; + } } assembleWellEqWithoutIteration(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);