From a94d66746240cc8240f8203713e6a8d037660978 Mon Sep 17 00:00:00 2001 From: Stein Krogstad Date: Thu, 10 Oct 2024 12:44:24 +0200 Subject: [PATCH 1/2] Don't stop wells unless global update agrees --- opm/simulators/wells/WellInterface_impl.hpp | 37 +++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 4ec6ccd67..78a03e334 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -827,11 +827,44 @@ namespace Opm // only use inner well iterations for the first newton iterations. const int iteration_idx = simulator.model().newtonMethod().numIterations(); if (iteration_idx < this->param_.max_niter_inner_well_iter_ || this->well_ecl_.isMultiSegment()) { + const auto& ws = well_state.well(this->indexOfWell()); + const auto pmode_orig = ws.production_cmode; + const auto imode_orig = ws.injection_cmode; + + Scalar qtotal_orig = 0.0; + const int np = well_state.numPhases(); + for (int p = 0; p < np; ++p) { + qtotal_orig += ws.surface_rates[p]; + } + this->operability_status_.solvable = true; bool converged = this->iterateWellEquations(simulator, 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 (converged) { + const bool zero_target = this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger); + if (this->wellIsStopped() && !zero_target && qtotal_orig != 0.0) { + // Well had non-zero rate, but was stopped during local well-solve. We re-open the well + // for the next global iteration, but if the zero rate persists, it will be stopped. + // This logic is introduced to prevent/ameliorate stopped/revived oscillations + this->operability_status_.resetOperability(); + this->openWell(); + deferred_logger.debug(" " + this->name() + " is re-opened after being stopped during local solve"); + } + // Add debug info for switched controls + if (ws.production_cmode != pmode_orig || ws.injection_cmode != imode_orig) { + std::string from,to; + if (this->isInjector()) { + from = WellInjectorCMode2String(imode_orig); + to = WellInjectorCMode2String(ws.injection_cmode); + } else { + from = WellProducerCMode2String(pmode_orig); + to = WellProducerCMode2String(ws.production_cmode); + } + deferred_logger.debug(" " + this->name() + " switched from " + from + " to " + to + " during local solve"); + } + + } else { + // unsolvable wells are treated as not operable and will not be solved for in this iteration. if (this->param_.shut_unsolvable_wells_) this->operability_status_.solvable = false; } From 6602c8be7fd9bedb99b9cd02e367cf61c039a3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 10 Oct 2024 16:14:07 +0200 Subject: [PATCH 2/2] Reformulate a nonzero rate criterion. --- opm/simulators/wells/WellInterface_impl.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 78a03e334..ecd01cd81 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -831,18 +832,17 @@ namespace Opm const auto pmode_orig = ws.production_cmode; const auto imode_orig = ws.injection_cmode; - Scalar qtotal_orig = 0.0; - const int np = well_state.numPhases(); - for (int p = 0; p < np; ++p) { - qtotal_orig += ws.surface_rates[p]; - } + const bool nonzero_rate_original = + std::any_of(ws.surface_rates.begin(), + ws.surface_rates.begin() + well_state.numPhases(), + [](Scalar rate) { return rate != Scalar(0.0); }); this->operability_status_.solvable = true; bool converged = this->iterateWellEquations(simulator, dt, well_state, group_state, deferred_logger); if (converged) { const bool zero_target = this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger); - if (this->wellIsStopped() && !zero_target && qtotal_orig != 0.0) { + if (this->wellIsStopped() && !zero_target && nonzero_rate_original) { // Well had non-zero rate, but was stopped during local well-solve. We re-open the well // for the next global iteration, but if the zero rate persists, it will be stopped. // This logic is introduced to prevent/ameliorate stopped/revived oscillations