From 427351cc12edefb01920aeb53309dc61b3f30814 Mon Sep 17 00:00:00 2001 From: Stein Krogstad Date: Tue, 19 Mar 2024 12:50:34 +0100 Subject: [PATCH] Include checking for zero targets from groups where required --- .../wells/BlackoilWellModel_impl.hpp | 23 ++-- opm/simulators/wells/MultisegmentWell.hpp | 10 +- .../wells/MultisegmentWellAssemble.cpp | 3 +- .../wells/MultisegmentWellAssemble.hpp | 1 + .../wells/MultisegmentWell_impl.hpp | 51 ++++---- opm/simulators/wells/StandardWell.hpp | 10 +- opm/simulators/wells/StandardWellAssemble.cpp | 3 +- opm/simulators/wells/StandardWellAssemble.hpp | 1 + opm/simulators/wells/StandardWell_impl.hpp | 87 +++++++------- opm/simulators/wells/WellInterface.hpp | 20 ++-- .../wells/WellInterfaceFluidSystem.cpp | 36 ++++++ .../wells/WellInterfaceFluidSystem.hpp | 6 + opm/simulators/wells/WellInterfaceGeneric.cpp | 14 +-- opm/simulators/wells/WellInterfaceGeneric.hpp | 7 +- opm/simulators/wells/WellInterface_impl.hpp | 112 +++++++----------- 15 files changed, 195 insertions(+), 189 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index f2cf467db..ba1a2a558 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -490,7 +490,7 @@ namespace Opm { // This is done only for producers, as injectors will only have a single // nonzero phase anyway. for (auto& well : well_container_) { - const bool zero_target = well->stopppedOrZeroRateTarget(summaryState, this->wellState()); + const bool zero_target = well->stoppedOrZeroRateTarget(simulator_, this->wellState(), local_deferredLogger); if (well->isProducer() && !zero_target) { well->updateWellStateRates(simulator_, this->wellState(), local_deferredLogger); } @@ -1076,8 +1076,7 @@ namespace Opm { } ++iter; for (auto& well : this->well_container_) { - const auto& summary_state = this->simulator_.vanguard().summaryState(); - well->solveEqAndUpdateWellState(summary_state, well_state, deferred_logger); + well->solveEqAndUpdateWellState(simulator_, well_state, deferred_logger); } this->initPrimaryVariablesEvaluation(); } while (iter < max_iter); @@ -1711,9 +1710,8 @@ namespace Opm { DeferredLogger local_deferredLogger; OPM_BEGIN_PARALLEL_TRY_CATCH(); { - const auto& summary_state = simulator_.vanguard().summaryState(); for (auto& well : well_container_) { - well->recoverWellSolutionAndUpdateWellState(summary_state, x, this->wellState(), local_deferredLogger); + well->recoverWellSolutionAndUpdateWellState(simulator_, x, this->wellState(), local_deferredLogger); } } OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger, @@ -1731,10 +1729,9 @@ namespace Opm { // try/catch here, as this function is not called in // parallel but for each individual domain of each rank. DeferredLogger local_deferredLogger; - const auto& summary_state = this->simulator_.vanguard().summaryState(); for (auto& well : well_container_) { if (well_domain_.at(well->name()) == domain.index) { - well->recoverWellSolutionAndUpdateWellState(summary_state, x, + well->recoverWellSolutionAndUpdateWellState(simulator_, x, this->wellState(), local_deferredLogger); } @@ -1782,7 +1779,6 @@ namespace Opm { const std::vector& B_avg, DeferredLogger& local_deferredLogger) const { - const auto& summary_state = simulator_.vanguard().summaryState(); const int iterationIdx = simulator_.model().newtonMethod().numIterations(); const bool relax_tolerance = iterationIdx > param_.strict_outer_iter_wells_; @@ -1790,7 +1786,7 @@ namespace Opm { for (const auto& well : well_container_) { if ((well_domain_.at(well->name()) == domain.index)) { if (well->isOperableAndSolvable() || well->wellIsStopped()) { - report += well->getWellConvergence(summary_state, + report += well->getWellConvergence(simulator_, this->wellState(), B_avg, local_deferredLogger, @@ -1833,9 +1829,8 @@ namespace Opm { const int iterationIdx = simulator_.model().newtonMethod().numIterations(); for (const auto& well : well_container_) { if (well->isOperableAndSolvable() || well->wellIsStopped()) { - const auto& summary_state = simulator_.vanguard().summaryState(); local_report += well->getWellConvergence( - summary_state, this->wellState(), B_avg, local_deferredLogger, + simulator_, this->wellState(), B_avg, local_deferredLogger, iterationIdx > param_.strict_outer_iter_wells_); } else { ConvergenceReport report; @@ -2356,8 +2351,7 @@ namespace Opm { auto& events = this->wellState().well(well->indexOfWell()).events; if (events.hasEvent(WellState::event_mask)) { well->updateWellStateWithTarget(simulator_, this->groupState(), this->wellState(), deferred_logger); - const auto& summary_state = simulator_.vanguard().summaryState(); - well->updatePrimaryVariables(summary_state, this->wellState(), deferred_logger); + well->updatePrimaryVariables(simulator_, this->wellState(), deferred_logger); well->initPrimaryVariablesEvaluation(); // There is no new well control change input within a report step, // so next time step, the well does not consider to have effective events anymore. @@ -2441,8 +2435,7 @@ namespace Opm { updatePrimaryVariables(DeferredLogger& deferred_logger) { for (const auto& well : well_container_) { - const auto& summary_state = simulator_.vanguard().summaryState(); - well->updatePrimaryVariables(summary_state, this->wellState(), deferred_logger); + well->updatePrimaryVariables(simulator_, this->wellState(), deferred_logger); } } diff --git a/opm/simulators/wells/MultisegmentWell.hpp b/opm/simulators/wells/MultisegmentWell.hpp index 385e369c5..da4c0f972 100644 --- a/opm/simulators/wells/MultisegmentWell.hpp +++ b/opm/simulators/wells/MultisegmentWell.hpp @@ -94,7 +94,7 @@ namespace Opm { DeferredLogger& deferred_logger) const override; /// check whether the well equations get converged for this well - ConvergenceReport getWellConvergence(const SummaryState& summary_state, + ConvergenceReport getWellConvergence(const Simulator& simulator, const WellState& well_state, const std::vector& B_avg, DeferredLogger& deferred_logger, @@ -107,7 +107,7 @@ namespace Opm { /// using the solution x to recover the solution xw for wells and applying /// xw to update Well State - void recoverWellSolutionAndUpdateWellState(const SummaryState& summary_state, + void recoverWellSolutionAndUpdateWellState(const Simulator& simulator, const BVector& x, WellState& well_state, DeferredLogger& deferred_logger) override; @@ -118,11 +118,11 @@ namespace Opm { std::vector& well_potentials, DeferredLogger& deferred_logger) override; - void updatePrimaryVariables(const SummaryState& summary_state, + void updatePrimaryVariables(const Simulator& simulator, const WellState& well_state, DeferredLogger& deferred_logger) override; - void solveEqAndUpdateWellState(const SummaryState& summary_state, + void solveEqAndUpdateWellState(const Simulator& simulator, WellState& well_state, DeferredLogger& deferred_logger) override; // const? @@ -174,7 +174,7 @@ namespace Opm { mutable int debug_cost_counter_ = 0; // updating the well_state based on well solution dwells - void updateWellState(const SummaryState& summary_state, + void updateWellState(const Simulator& simulator, const BVectorWell& dwells, WellState& well_state, DeferredLogger& deferred_logger, diff --git a/opm/simulators/wells/MultisegmentWellAssemble.cpp b/opm/simulators/wells/MultisegmentWellAssemble.cpp index f2cae4313..83f76d416 100644 --- a/opm/simulators/wells/MultisegmentWellAssemble.cpp +++ b/opm/simulators/wells/MultisegmentWellAssemble.cpp @@ -92,6 +92,7 @@ assembleControlEq(const WellState& well_state, const Scalar rho, const PrimaryVariables& primary_variables, Equations& eqns1, + const bool stopped_or_zero_target, DeferredLogger& deferred_logger) const { static constexpr int Gas = BlackoilPhases::Vapour; @@ -116,7 +117,7 @@ assembleControlEq(const WellState& well_state, return rates; }; - if (well_.stopppedOrZeroRateTarget(summaryState, well_state)) { + if (stopped_or_zero_target) { control_eq = primary_variables.getWQTotal(); } else if (well_.isInjector() ) { // Find scaling factor to get injection rate, diff --git a/opm/simulators/wells/MultisegmentWellAssemble.hpp b/opm/simulators/wells/MultisegmentWellAssemble.hpp index 1c8196734..5ce922c72 100644 --- a/opm/simulators/wells/MultisegmentWellAssemble.hpp +++ b/opm/simulators/wells/MultisegmentWellAssemble.hpp @@ -79,6 +79,7 @@ public: const Scalar rho, const PrimaryVariables& primary_variables, Equations& eqns, + const bool stopped_or_zero_target, DeferredLogger& deferred_logger) const; //! \brief Assemble piece of the acceleration term diff --git a/opm/simulators/wells/MultisegmentWell_impl.hpp b/opm/simulators/wells/MultisegmentWell_impl.hpp index 40e89e6bc..da4f8ad72 100644 --- a/opm/simulators/wells/MultisegmentWell_impl.hpp +++ b/opm/simulators/wells/MultisegmentWell_impl.hpp @@ -162,11 +162,11 @@ namespace Opm template void MultisegmentWell:: - updatePrimaryVariables(const SummaryState& summary_state, + updatePrimaryVariables(const Simulator& simulator, const WellState& well_state, - DeferredLogger& /* deferred_logger */) + DeferredLogger& deferred_logger) { - const bool stop_or_zero_rate_target = this->stopppedOrZeroRateTarget(summary_state, well_state); + const bool stop_or_zero_rate_target = this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger); this->primary_variables_.update(well_state, stop_or_zero_rate_target); } @@ -199,7 +199,7 @@ namespace Opm template ConvergenceReport MultisegmentWell:: - getWellConvergence(const SummaryState& /* summary_state */, + getWellConvergence(const Simulator& /* simulator */, const WellState& well_state, const std::vector& B_avg, DeferredLogger& deferred_logger, @@ -260,7 +260,7 @@ namespace Opm template void MultisegmentWell:: - recoverWellSolutionAndUpdateWellState(const SummaryState& summary_state, + recoverWellSolutionAndUpdateWellState(const Simulator& simulator, const BVector& x, WellState& well_state, DeferredLogger& deferred_logger) @@ -271,7 +271,7 @@ namespace Opm BVectorWell xw(1); this->linSys_.recoverSolutionWell(x, xw); - updateWellState(summary_state, xw, well_state, deferred_logger); + updateWellState(simulator, xw, well_state, deferred_logger); } @@ -578,7 +578,7 @@ namespace Opm template void MultisegmentWell:: - solveEqAndUpdateWellState(const SummaryState& summary_state, + solveEqAndUpdateWellState(const Simulator& simulator, WellState& well_state, DeferredLogger& deferred_logger) { @@ -589,7 +589,7 @@ namespace Opm try{ const BVectorWell dx_well = this->linSys_.solve(); - updateWellState(summary_state, dx_well, well_state, deferred_logger); + updateWellState(simulator, dx_well, well_state, deferred_logger); } catch(const NumericalProblem& exp) { // Add information about the well and log to deferred logger @@ -681,7 +681,7 @@ namespace Opm template void MultisegmentWell:: - updateWellState(const SummaryState& summary_state, + updateWellState(const Simulator& simulator, const BVectorWell& dwells, WellState& well_state, DeferredLogger& deferred_logger, @@ -691,15 +691,20 @@ namespace Opm const Scalar dFLimit = this->param_.dwell_fraction_max_; const Scalar max_pressure_change = this->param_.max_pressure_change_ms_wells_; - const bool stop_or_zero_rate_target = this->stopppedOrZeroRateTarget(summary_state, well_state); + const bool stop_or_zero_rate_target = + this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger); this->primary_variables_.updateNewton(dwells, relaxation_factor, dFLimit, stop_or_zero_rate_target, max_pressure_change); - this->primary_variables_.copyToWellState(*this, getRefDensity(), stop_or_zero_rate_target, - well_state, summary_state, deferred_logger); + const auto& summary_state = simulator.vanguard().summaryState(); + this->primary_variables_.copyToWellState(*this, getRefDensity(), + stop_or_zero_rate_target, + well_state, + summary_state, + deferred_logger); { auto& ws = well_state.well(this->index_of_well_); @@ -720,8 +725,7 @@ namespace Opm const WellState& well_state, DeferredLogger& deferred_logger) { - const auto& summary_state = simulator.vanguard().summaryState(); - updatePrimaryVariables(summary_state, well_state, deferred_logger); + updatePrimaryVariables(simulator, well_state, deferred_logger); initPrimaryVariablesEvaluation(); computePerfCellPressDiffs(simulator); computeInitialSegmentFluids(simulator); @@ -1489,8 +1493,7 @@ namespace Opm this->regularize_ = true; } - const auto& summary_state = simulator.vanguard().summaryState(); - const auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence); + const auto report = getWellConvergence(simulator, well_state, Base::B_avg_, deferred_logger, relax_convergence); if (report.converged()) { converged = true; break; @@ -1526,7 +1529,7 @@ namespace Opm ++stagnate_count; if (stagnate_count == 6) { sstr << " well " << this->name() << " observes severe stagnation and/or oscillation. We relax the tolerance and check for convergence. \n"; - const auto reportStag = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, true); + const auto reportStag = getWellConvergence(simulator, well_state, Base::B_avg_, deferred_logger, true); if (reportStag.converged()) { converged = true; sstr << " well " << this->name() << " manages to get converged with relaxed tolerances in " << it << " inner iterations"; @@ -1553,7 +1556,7 @@ namespace Opm this->regularize_ = true; deferred_logger.debug(sstr.str()); } - updateWellState(summary_state, dx_well, well_state, deferred_logger, relaxation_factor); + updateWellState(simulator, dx_well, well_state, deferred_logger, relaxation_factor); initPrimaryVariablesEvaluation(); } @@ -1635,7 +1638,7 @@ namespace Opm const bool allow_open = this->well_ecl_.getStatus() == WellStatus::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 - const bool allow_switching = !this->wellUnderZeroRateTarget(summary_state, well_state) && + const bool allow_switching = !this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger) && (!fixed_control || !fixed_status) && allow_open; bool changed = false; bool final_check = false; @@ -1673,7 +1676,7 @@ namespace Opm this->regularize_ = true; } - const auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence); + const auto report = getWellConvergence(simulator, well_state, Base::B_avg_, deferred_logger, relax_convergence); converged = report.converged(); if (converged) { // if equations are sufficiently linear they might converge in less than min_its_after_switch @@ -1717,7 +1720,7 @@ namespace Opm if (false) { // this disables the usage of the relaxed tolerance fmt::format_to(std::back_inserter(message), " Well {} observes severe stagnation and/or oscillation." " We relax the tolerance and check for convergence. \n", this->name()); - const auto reportStag = getWellConvergence(summary_state, well_state, Base::B_avg_, + const auto reportStag = getWellConvergence(simulator, well_state, Base::B_avg_, deferred_logger, true); if (reportStag.converged()) { converged = true; @@ -1745,7 +1748,7 @@ namespace Opm deferred_logger.debug(message); } } - updateWellState(summary_state, dx_well, well_state, deferred_logger, relaxation_factor); + updateWellState(simulator, dx_well, well_state, deferred_logger, relaxation_factor); initPrimaryVariablesEvaluation(); } @@ -1906,10 +1909,11 @@ namespace Opm } } - // the fourth dequation, the pressure drop equation + // the fourth equation, the pressure drop equation if (seg == 0) { // top segment, pressure equation is the control equation const auto& summaryState = simulator.vanguard().summaryState(); const Schedule& schedule = simulator.vanguard().schedule(); + const bool stopped_or_zero_target = this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger); MultisegmentWellAssemble(*this). assembleControlEq(well_state, group_state, @@ -1920,6 +1924,7 @@ namespace Opm getRefDensity(), this->primary_variables_, this->linSys_, + stopped_or_zero_target, deferred_logger); } else { const UnitSystem& unit_system = simulator.vanguard().eclState().getDeckUnitSystem(); diff --git a/opm/simulators/wells/StandardWell.hpp b/opm/simulators/wells/StandardWell.hpp index 846407a20..464bcaa63 100644 --- a/opm/simulators/wells/StandardWell.hpp +++ b/opm/simulators/wells/StandardWell.hpp @@ -140,7 +140,7 @@ namespace Opm void initPrimaryVariablesEvaluation() override; /// check whether the well equations get converged for this well - virtual ConvergenceReport getWellConvergence(const SummaryState& summary_state, + virtual ConvergenceReport getWellConvergence(const Simulator& simulator, const WellState& well_state, const std::vector& B_avg, DeferredLogger& deferred_logger, @@ -153,7 +153,7 @@ namespace Opm /// using the solution x to recover the solution xw for wells and applying /// xw to update Well State - void recoverWellSolutionAndUpdateWellState(const SummaryState& summary_state, + void recoverWellSolutionAndUpdateWellState(const Simulator& simulator, const BVector& x, WellState& well_state, DeferredLogger& deferred_logger) override; @@ -164,11 +164,11 @@ namespace Opm std::vector& well_potentials, DeferredLogger& deferred_logger) /* const */ override; - void updatePrimaryVariables(const SummaryState& summary_state, + void updatePrimaryVariables(const Simulator& simulator, const WellState& well_state, DeferredLogger& deferred_logger) override; - void solveEqAndUpdateWellState(const SummaryState& summary_state, + void solveEqAndUpdateWellState(const Simulator& simulator, WellState& well_state, DeferredLogger& deferred_logger) override; @@ -262,7 +262,7 @@ namespace Opm bool regularize_; // updating the well_state based on well solution dwells - void updateWellState(const SummaryState& summary_state, + void updateWellState(const Simulator& simulator, const BVectorWell& dwells, WellState& well_state, DeferredLogger& deferred_logger); diff --git a/opm/simulators/wells/StandardWellAssemble.cpp b/opm/simulators/wells/StandardWellAssemble.cpp index 263a7b3e4..0ee742301 100644 --- a/opm/simulators/wells/StandardWellAssemble.cpp +++ b/opm/simulators/wells/StandardWellAssemble.cpp @@ -93,6 +93,7 @@ assembleControlEq(const WellState& well_state, const PrimaryVariables& primary_variables, const Scalar rho, StandardWellEquations& eqns1, + const bool stopped_or_zero_target, DeferredLogger& deferred_logger) const { static constexpr int Water = BlackoilPhases::Aqua; @@ -119,7 +120,7 @@ assembleControlEq(const WellState& well_state, return rates; }; - if (well_.stopppedOrZeroRateTarget(summaryState, well_state)) { + if (stopped_or_zero_target) { control_eq = primary_variables.eval(PrimaryVariables::WQTotal); } else if (well_.isInjector()) { // Find injection rate. diff --git a/opm/simulators/wells/StandardWellAssemble.hpp b/opm/simulators/wells/StandardWellAssemble.hpp index fc4d416de..41c166363 100644 --- a/opm/simulators/wells/StandardWellAssemble.hpp +++ b/opm/simulators/wells/StandardWellAssemble.hpp @@ -61,6 +61,7 @@ public: const PrimaryVariables& primary_variables, const Scalar rho, StandardWellEquations& eqns, + const bool stopped_or_zero_target, DeferredLogger& deferred_logger) const; //! \brief Assemble injectivity equation. diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index edc4e553f..fcc50e2fc 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -462,6 +462,7 @@ namespace Opm const auto& summaryState = simulator.vanguard().summaryState(); const Schedule& schedule = simulator.vanguard().schedule(); + const bool stopped_or_zero_target = this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger); StandardWellAssemble(*this). assembleControlEq(well_state, group_state, schedule, summaryState, @@ -469,6 +470,7 @@ namespace Opm this->primary_variables_, this->connections_.rho(), this->linSys_, + stopped_or_zero_target, deferred_logger); @@ -692,16 +694,17 @@ namespace Opm template void StandardWell:: - updateWellState(const SummaryState& summary_state, + updateWellState(const Simulator& simulator, const BVectorWell& dwells, WellState& well_state, DeferredLogger& deferred_logger) { if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return; - const bool stop_or_zero_rate_target = this->stopppedOrZeroRateTarget(summary_state, well_state); + const bool stop_or_zero_rate_target = this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger); updatePrimaryVariablesNewton(dwells, stop_or_zero_rate_target, deferred_logger); + const auto& summary_state = simulator.vanguard().summaryState(); updateWellStateFromPrimaryVariables(stop_or_zero_rate_target, well_state, summary_state, deferred_logger); Base::calculateReservoirRates(well_state.well(this->index_of_well_)); } @@ -805,7 +808,10 @@ namespace Opm // the well index associated with the connection Scalar trans_mult = simulator.problem().template wellTransMultiplier(int_quantities, cell_idx); const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_); - const std::vector tw_perf = this->wellIndex(perf, int_quantities, trans_mult, wellstate_nupcol); + const std::vector tw_perf = this->wellIndex(perf, + int_quantities, + trans_mult, + wellstate_nupcol); std::vector ipr_a_perf(this->ipr_a_.size()); std::vector ipr_b_perf(this->ipr_b_.size()); for (int comp_idx = 0; comp_idx < this->num_components_; ++comp_idx) { @@ -849,11 +855,11 @@ namespace Opm updateIPRImplicit(const Simulator& simulator, WellState& well_state, DeferredLogger& deferred_logger) - { + { // Compute IPR based on *converged* well-equation: - // For a component rate r the derivative dr/dbhp is obtained by + // For a component rate r the derivative dr/dbhp is obtained by // dr/dbhp = - (partial r/partial x) * inv(partial Eq/partial x) * (partial Eq/partial bhp_target) - // where Eq(x)=0 is the well equation setup with bhp control and primary variables x + // where Eq(x)=0 is the well equation setup with bhp control and primary variables x // We shouldn't have zero rates at this stage, but check bool zero_rates; @@ -867,7 +873,7 @@ namespace Opm const auto msg = fmt::format("updateIPRImplicit: Well {} has zero rate, IPRs might be problematic", this->name()); deferred_logger.debug(msg); /* - // could revert to standard approach here: + // could revert to standard approach here: updateIPR(simulator, deferred_logger); for (int comp_idx = 0; comp_idx < this->num_components_; ++comp_idx){ const int idx = this->modelCompIdxToFlowCompIdx(comp_idx); @@ -876,12 +882,12 @@ namespace Opm } return; */ - } + } const auto& group_state = simulator.problem().wellModel().groupState(); std::fill(ws.implicit_ipr_a.begin(), ws.implicit_ipr_a.end(), 0.); std::fill(ws.implicit_ipr_b.begin(), ws.implicit_ipr_b.end(), 0.); - + auto inj_controls = Well::InjectionControls(0); auto prod_controls = Well::ProductionControls(0); prod_controls.addControl(Well::ProducerCMode::BHP); @@ -898,7 +904,7 @@ namespace Opm rhs[0].resize(nEq); // rhs = 0 except -1 for control eq for (size_t i=0; i < nEq; ++i){ - rhs[0][i] = 0.0; + rhs[0][i] = 0.0; } rhs[0][Bhp] = -1.0; @@ -910,7 +916,7 @@ namespace Opm EvalWell comp_rate = this->primary_variables_.getQs(comp_idx); const int idx = this->modelCompIdxToFlowCompIdx(comp_idx); for (size_t pvIdx = 0; pvIdx < nEq; ++pvIdx) { - // well primary variable derivatives in EvalWell start at position Indices::numEq + // well primary variable derivatives in EvalWell start at position Indices::numEq ws.implicit_ipr_b[idx] -= x_well[0][pvIdx]*comp_rate.derivative(pvIdx+Indices::numEq); } ws.implicit_ipr_a[idx] = ws.implicit_ipr_b[idx]*ws.bhp - comp_rate.value(); @@ -1175,7 +1181,7 @@ namespace Opm template ConvergenceReport StandardWell:: - getWellConvergence(const SummaryState& summary_state, + getWellConvergence(const Simulator& simulator, const WellState& well_state, const std::vector& B_avg, DeferredLogger& deferred_logger, @@ -1190,7 +1196,7 @@ namespace Opm constexpr Scalar stopped_factor = 1.e-4; // use stricter tolerance for dynamic thp to ameliorate network convergence constexpr Scalar dynamic_thp_factor = 1.e-1; - if (this->stopppedOrZeroRateTarget(summary_state, well_state)) { + if (this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger)) { tol_wells = tol_wells*stopped_factor; } else if (this->getDynamicThpLimit()) { tol_wells = tol_wells*dynamic_thp_factor; @@ -1357,7 +1363,7 @@ namespace Opm template void StandardWell:: - solveEqAndUpdateWellState(const SummaryState& summary_state, + solveEqAndUpdateWellState(const Simulator& simulator, WellState& well_state, DeferredLogger& deferred_logger) { @@ -1369,7 +1375,7 @@ namespace Opm dx_well[0].resize(this->primary_variables_.numWellEq()); this->linSys_.solve( dx_well); - updateWellState(summary_state, dx_well, well_state, deferred_logger); + updateWellState(simulator, dx_well, well_state, deferred_logger); } @@ -1383,8 +1389,7 @@ namespace Opm const WellState& well_state, DeferredLogger& deferred_logger) { - const auto& summary_state = simulator.vanguard().summaryState(); - updatePrimaryVariables(summary_state, well_state, deferred_logger); + updatePrimaryVariables(simulator, well_state, deferred_logger); initPrimaryVariablesEvaluation(); computeWellConnectionPressures(simulator, well_state, deferred_logger); this->computeAccumWell(); @@ -1427,7 +1432,7 @@ namespace Opm template void StandardWell:: - recoverWellSolutionAndUpdateWellState(const SummaryState& summary_state, + recoverWellSolutionAndUpdateWellState(const Simulator& simulator, const BVector& x, WellState& well_state, DeferredLogger& deferred_logger) @@ -1438,7 +1443,7 @@ namespace Opm xw[0].resize(this->primary_variables_.numWellEq()); this->linSys_.recoverSolutionWell(x, xw); - updateWellState(summary_state, xw, well_state, deferred_logger); + updateWellState(simulator, xw, well_state, deferred_logger); } @@ -1535,7 +1540,7 @@ namespace Opm well_state_copy.wellRates(this->index_of_well_)[phase] = sign * ws.well_potentials[phase]; } - well_copy.updatePrimaryVariables(summary_state, well_state_copy, deferred_logger); + well_copy.updatePrimaryVariables(simulator, well_state_copy, deferred_logger); well_copy.initPrimaryVariablesEvaluation(); well_copy.computeAccumWell(); @@ -1546,7 +1551,7 @@ namespace Opm " potentials are computed based on unconverged solution"; deferred_logger.debug(msg); } - well_copy.updatePrimaryVariables(summary_state, well_state_copy, deferred_logger); + well_copy.updatePrimaryVariables(simulator, well_state_copy, deferred_logger); well_copy.computeWellConnectionPressures(simulator, well_state_copy, deferred_logger); well_copy.initPrimaryVariablesEvaluation(); well_copy.computeWellRatesWithBhp(simulator, bhp, well_flux, deferred_logger); @@ -1598,7 +1603,7 @@ namespace Opm DeferredLogger& deferred_logger) const { // Create a copy of the well. - // TODO: check if we can avoid taking multiple copies. Call from updateWellPotentials + // TODO: check if we can avoid taking multiple copies. Call from updateWellPotentials // is allready a copy, but not from other calls. StandardWell well_copy(*this); @@ -1618,7 +1623,7 @@ namespace Opm // prepare/modify well state and control well_copy.prepareForPotentialCalculations(summary_state, well_state_copy, inj_controls, prod_controls); - + // initialize rates from previous potentials const int np = this->number_of_phases_; bool trivial = true; @@ -1718,7 +1723,7 @@ namespace Opm if (this->param_.local_well_solver_control_switching_) { converged_implicit = computeWellPotentialsImplicit(simulator, well_potentials, deferred_logger); } - if (!converged_implicit) { + if (!converged_implicit) { // does the well have a THP related constraint? const auto& summaryState = simulator.vanguard().summaryState(); if (!Base::wellHasTHPConstraints(summaryState) || bhp_controlled_well) { @@ -1774,13 +1779,13 @@ namespace Opm template void StandardWell:: - updatePrimaryVariables(const SummaryState& summary_state, + updatePrimaryVariables(const Simulator& simulator, const WellState& well_state, DeferredLogger& deferred_logger) { if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return; - const bool stop_or_zero_rate_target = this->stopppedOrZeroRateTarget(summary_state, well_state); + const bool stop_or_zero_rate_target = this->stoppedOrZeroRateTarget(simulator, well_state, deferred_logger); this->primary_variables_.update(well_state, stop_or_zero_rate_target, deferred_logger); // other primary variables related to polymer injection @@ -1841,7 +1846,7 @@ namespace Opm PerforationRates perf_rates; Scalar trans_mult = simulator.problem().template wellTransMultiplier(int_quant, cell_idx); const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_); - const std::vector Tw = this->wellIndex(perf, int_quant, trans_mult, wellstate_nupcol); + const std::vector Tw = this->wellIndex(perf, int_quant, trans_mult, wellstate_nupcol); computePerfRate(int_quant, mob, bhp, Tw, perf, allow_cf, cq_s, perf_rates, deferred_logger); // TODO: make area a member @@ -2300,7 +2305,6 @@ namespace Opm bool converged; bool relax_convergence = false; this->regularize_ = false; - const auto& summary_state = simulator.vanguard().summaryState(); do { assembleWellEqWithoutIteration(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); @@ -2309,7 +2313,7 @@ namespace Opm this->regularize_ = true; } - auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence); + auto report = getWellConvergence(simulator, well_state, Base::B_avg_, deferred_logger, relax_convergence); converged = report.converged(); if (converged) { @@ -2317,7 +2321,7 @@ namespace Opm } ++it; - solveEqAndUpdateWellState(summary_state, well_state, deferred_logger); + solveEqAndUpdateWellState(simulator, well_state, deferred_logger); // TODO: when this function is used for well testing purposes, will need to check the controls, so that we will obtain convergence // under the most restrictive control. Based on this converged results, we can check whether to re-open the well. Either we refactor @@ -2340,8 +2344,8 @@ namespace Opm const Well::ProductionControls& prod_controls, WellState& well_state, const GroupState& group_state, - DeferredLogger& deferred_logger, - const bool fixed_control /*false*/, + DeferredLogger& deferred_logger, + const bool fixed_control /*false*/, const bool fixed_status /*false*/) { const int max_iter = this->param_.max_inner_iter_wells_; @@ -2352,8 +2356,8 @@ namespace Opm const auto& summary_state = simulator.vanguard().summaryState(); // Always take a few (more than one) iterations after a switch before allowing a new switch - // 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 + // 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; int its_since_last_switch = min_its_after_switch; int switch_count= 0; @@ -2366,11 +2370,10 @@ namespace Opm const bool allow_open = this->well_ecl_.getStatus() == WellStatus::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 - //const bool allow_switching = !this->wellUnderZeroRateTarget(summary_state, well_state) && - // (!fixed_control || !fixed_status) && allow_open; - const bool allow_switching = !this->wellUnderZeroRateTargetVersion(simulator, well_state, deferred_logger) && - (!fixed_control || !fixed_status) && allow_open; - + const bool allow_switching = + !this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger) && + (!fixed_control || !fixed_status) && allow_open; + bool changed = false; bool final_check = false; // well needs to be set operable or else solving/updating of re-opened wells is skipped @@ -2405,7 +2408,7 @@ namespace Opm this->regularize_ = true; } - auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence); + auto report = getWellConvergence(simulator, well_state, Base::B_avg_, deferred_logger, relax_convergence); converged = report.converged(); if (converged) { @@ -2420,11 +2423,11 @@ namespace Opm } ++it; - solveEqAndUpdateWellState(summary_state, well_state, deferred_logger); + solveEqAndUpdateWellState(simulator, well_state, deferred_logger); initPrimaryVariablesEvaluation(); } while (it < max_iter); - + if (converged) { if (allow_switching){ // update operability if status change diff --git a/opm/simulators/wells/WellInterface.hpp b/opm/simulators/wells/WellInterface.hpp index 4ace035ef..2dcaa4ccf 100644 --- a/opm/simulators/wells/WellInterface.hpp +++ b/opm/simulators/wells/WellInterface.hpp @@ -155,13 +155,13 @@ public: virtual void initPrimaryVariablesEvaluation() = 0; - virtual ConvergenceReport getWellConvergence(const SummaryState& summary_state, + virtual ConvergenceReport getWellConvergence(const Simulator& simulator, const WellState& well_state, const std::vector& B_avg, DeferredLogger& deferred_logger, const bool relax_tolerance) const = 0; - virtual void solveEqAndUpdateWellState(const SummaryState& summary_state, + virtual void solveEqAndUpdateWellState(const Simulator& simulator, WellState& well_state, DeferredLogger& deferred_logger) = 0; @@ -198,7 +198,7 @@ public: /// using the solution x to recover the solution xw for wells and applying /// xw to update Well State - virtual void recoverWellSolutionAndUpdateWellState(const SummaryState& summary_state, + virtual void recoverWellSolutionAndUpdateWellState(const Simulator& simulator, const BVector& x, WellState& well_state, DeferredLogger& deferred_logger) = 0; @@ -225,13 +225,13 @@ public: std::vector& well_flux, DeferredLogger& deferred_logger) const = 0; - bool wellUnderZeroRateTargetVersion(const Simulator& simulator, - const WellState& well_state, - DeferredLogger& deferred_logger) const; + bool wellUnderZeroRateTarget(const Simulator& simulator, + const WellState& well_state, + DeferredLogger& deferred_logger) const; - bool stoppedOrZeroRateTargetVersion(const Simulator& simulator, - const WellState& well_state, - DeferredLogger& deferred_logger) const; + bool stoppedOrZeroRateTarget(const Simulator& simulator, + const WellState& well_state, + DeferredLogger& deferred_logger) const; bool updateWellStateWithTHPTargetProd(const Simulator& simulator, WellState& well_state, @@ -254,7 +254,7 @@ public: const bool fixed_control = false, const bool fixed_status = false); - virtual void updatePrimaryVariables(const SummaryState& summary_state, + virtual void updatePrimaryVariables(const Simulator& simulator, const WellState& well_state, DeferredLogger& deferred_logger) = 0; diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index a377e9e68..7c97994a3 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -305,6 +305,42 @@ getGroupProductionTargetRate(const Group& group, deferred_logger); } +template +bool +WellInterfaceFluidSystem:: +wellUnderZeroRateTargetGroup(const SummaryState& summary_state, + const Schedule& schedule, + const WellState& well_state, + const GroupState& group_state, + DeferredLogger& deferred_logger) const +{ + const auto& well = this->well_ecl_; + const auto& group = schedule.getGroup(well.groupName(), this->currentStep()); + const Scalar efficiencyFactor = well.getEfficiencyFactor(); + if (this->isInjector()) { + // Check injector under group control + const auto& controls = well.injectionControls(summary_state); + const std::optional target = + this->getGroupInjectionTargetRate(group, well_state, + group_state, schedule, + summary_state, controls.injector_type, + efficiencyFactor, deferred_logger); + if (target.has_value()) { + return target.value() == 0.0; + } else { + return false; + } + } else { + // Check producer under group control + const Scalar scale = + this->getGroupProductionTargetRate(group, well_state, + group_state, schedule, + summary_state, efficiencyFactor, + deferred_logger); + return scale == 0.0; + } +} + template class WellInterfaceFluidSystem>; } // namespace Opm diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.hpp b/opm/simulators/wells/WellInterfaceFluidSystem.hpp index c2c15f242..0a29acd9e 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.hpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.hpp @@ -117,6 +117,12 @@ protected: Scalar efficiencyFactor, DeferredLogger& deferred_logger) const; + bool wellUnderZeroRateTargetGroup(const SummaryState& summary_state, + const Schedule& schedule, + const WellState& well_state, + const GroupState& group_state, + DeferredLogger& deferredLogger) const; + // For the conversion between the surface volume rate and reservoir voidage rate const RateConverterType& rateConverter_; }; diff --git a/opm/simulators/wells/WellInterfaceGeneric.cpp b/opm/simulators/wells/WellInterfaceGeneric.cpp index 5c0417544..7935bb18f 100644 --- a/opm/simulators/wells/WellInterfaceGeneric.cpp +++ b/opm/simulators/wells/WellInterfaceGeneric.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -605,8 +606,8 @@ isPressureControlled(const WellState& well_state) const template bool WellInterfaceGeneric:: -wellUnderZeroRateTarget(const SummaryState& summary_state, - const WellState& well_state) const +wellUnderZeroRateTargetIndividual(const SummaryState& summary_state, + const WellState& well_state) const { if (this->isProducer()) { // producers const auto prod_controls = this->well_ecl_.productionControls(summary_state); @@ -619,15 +620,6 @@ wellUnderZeroRateTarget(const SummaryState& summary_state, } } -template -bool WellInterfaceGeneric:: -stopppedOrZeroRateTarget(const SummaryState& summary_state, - const WellState& well_state) const -{ - return (this->wellIsStopped() || this->wellUnderZeroRateTarget(summary_state, well_state)); - -} - template void WellInterfaceGeneric::resetWellOperability() { diff --git a/opm/simulators/wells/WellInterfaceGeneric.hpp b/opm/simulators/wells/WellInterfaceGeneric.hpp index 440561e5d..edbd57312 100644 --- a/opm/simulators/wells/WellInterfaceGeneric.hpp +++ b/opm/simulators/wells/WellInterfaceGeneric.hpp @@ -173,9 +173,6 @@ public: bool isPressureControlled(const WellState& well_state) const; - bool stopppedOrZeroRateTarget(const SummaryState& summary_state, - const WellState& well_state) const; - Scalar wellEfficiencyFactor() const { return well_efficiency_factor_; } //! \brief Update filter cake multipliers. @@ -200,8 +197,8 @@ protected: int polymerInjTable_() const; int polymerWaterTable_() const; - bool wellUnderZeroRateTarget(const SummaryState& summary_state, - const WellState& well_state) const; + bool wellUnderZeroRateTargetIndividual(const SummaryState& summary_state, + const WellState& well_state) const; std::pair computeWellPotentials(std::vector& well_potentials, diff --git a/opm/simulators/wells/WellInterface_impl.hpp b/opm/simulators/wells/WellInterface_impl.hpp index 2bbc9c566..9e93dad34 100644 --- a/opm/simulators/wells/WellInterface_impl.hpp +++ b/opm/simulators/wells/WellInterface_impl.hpp @@ -198,8 +198,7 @@ namespace Opm const GroupState& group_state, DeferredLogger& deferred_logger) /* const */ { - const auto& summary_state = simulator.vanguard().summaryState(); - if (this->stopppedOrZeroRateTarget(summary_state, well_state)) { + if (stoppedOrZeroRateTarget(simulator, well_state, deferred_logger)) { return false; } @@ -260,7 +259,7 @@ namespace Opm this->well_control_log_.push_back(from); updateWellStateWithTarget(simulator, group_state, well_state, deferred_logger); - updatePrimaryVariables(summaryState, well_state, deferred_logger); + updatePrimaryVariables(simulator, well_state, deferred_logger); } return changed; @@ -282,7 +281,7 @@ namespace Opm const auto& summary_state = simulator.vanguard().summaryState(); const auto& schedule = simulator.vanguard().schedule(); - if (this->wellUnderZeroRateTarget(summary_state, well_state) || !(this->well_ecl_.getStatus() == WellStatus::OPEN)) { + if (this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger) || !(this->well_ecl_.getStatus() == WellStatus::OPEN)) { return false; } @@ -311,8 +310,8 @@ namespace Opm updateWellStateWithTarget(simulator, group_state, well_state, deferred_logger); } else { ws.thp = this->getTHPConstraint(summary_state); - } - updatePrimaryVariables(summary_state, well_state, deferred_logger); + } + updatePrimaryVariables(simulator, well_state, deferred_logger); } } return changed; @@ -377,8 +376,7 @@ namespace Opm updateWellStateWithTarget(simulator, group_state, well_state_copy, deferred_logger); calculateExplicitQuantities(simulator, well_state_copy, deferred_logger); - const auto& summary_state = simulator.vanguard().summaryState(); - updatePrimaryVariables(summary_state, well_state_copy, deferred_logger); + updatePrimaryVariables(simulator, well_state_copy, deferred_logger); initPrimaryVariablesEvaluation(); if (this->isProducer()) { @@ -539,13 +537,13 @@ namespace Opm const bool isThp = ws.production_cmode == Well::ProducerCMode::THP; // check stability of solution under thp-control - if (converged && !this->stopppedOrZeroRateTarget(summary_state, well_state) && isThp) { + if (converged && !stoppedOrZeroRateTarget(simulator, well_state, deferred_logger) && isThp) { auto rates = well_state.well(this->index_of_well_).surface_rates; this->adaptRatesForVFP(rates); this->updateIPRImplicit(simulator, well_state, deferred_logger); bool is_stable = WellBhpThpCalculator(*this).isStableSolution(well_state, this->well_ecl_, rates, summary_state); if (!is_stable) { - // solution converged to an unstable point! + // solution converged to an unstable point! this->operability_status_.use_vfpexplicit = true; auto bhp_stable = WellBhpThpCalculator(*this).estimateStableBhp(well_state, this->well_ecl_, rates, this->getRefDensity(), summary_state); // if we find an intersection with a sufficiently lower bhp, re-solve equations @@ -570,10 +568,10 @@ namespace Opm if (!bhp_target.has_value()) { // well can't operate using explicit fractions is_operable = false; - // solve with zero rate + // solve with zero rate converged = solveWellWithZeroRate(simulator, dt, well_state, deferred_logger); this->stopWell(); - } else { + } else { // solve well with the estimated target bhp (or limit) const Scalar bhp = std::max(bhp_target.value(), static_cast(prod_controls.bhp_limit)); @@ -602,8 +600,8 @@ namespace Opm WellState& well_state, const SummaryState& summary_state, DeferredLogger& deferred_logger) - { - // Given an unconverged well or closed well, estimate an operable bhp (if any) + { + // Given an unconverged well or closed well, estimate an operable bhp (if any) // Get minimal bhp from vfp-curve Scalar bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity()); // Solve @@ -615,7 +613,7 @@ namespace Opm auto rates = well_state.well(this->index_of_well_).surface_rates; this->adaptRatesForVFP(rates); return WellBhpThpCalculator(*this).estimateStableBhp(well_state, this->well_ecl_, rates, this->getRefDensity(), summary_state); - } + } template bool @@ -625,11 +623,11 @@ namespace Opm const Scalar bhp, WellState& well_state, DeferredLogger& deferred_logger) - { + { // Solve a well using single bhp-constraint (but close if not operable under this) auto group_state = GroupState(); // empty group auto inj_controls = Well::InjectionControls(0); - auto prod_controls = Well::ProductionControls(0); + auto prod_controls = Well::ProductionControls(0); auto& ws = well_state.well(this->index_of_well_); auto cmode_inj = ws.injection_cmode; auto cmode_prod = ws.production_cmode; @@ -637,21 +635,21 @@ namespace Opm inj_controls.addControl(Well::InjectorCMode::BHP); inj_controls.bhp_limit = bhp; inj_controls.cmode = Well::InjectorCMode::BHP; - ws.injection_cmode = Well::InjectorCMode::BHP; + ws.injection_cmode = Well::InjectorCMode::BHP; } else { prod_controls.addControl(Well::ProducerCMode::BHP); prod_controls.bhp_limit = bhp; prod_controls.cmode = Well::ProducerCMode::BHP; - ws.production_cmode = Well::ProducerCMode::BHP; + ws.production_cmode = Well::ProducerCMode::BHP; } // update well-state ws.bhp = bhp; - // solve + // solve const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger, /*fixed_control*/true); ws.injection_cmode = cmode_inj; ws.production_cmode = cmode_prod; return converged; - } + } template bool @@ -660,18 +658,18 @@ namespace Opm const double dt, WellState& well_state, DeferredLogger& deferred_logger) - { + { // Solve a well as stopped const auto well_status_orig = this->wellStatus_; this->stopWell(); - + auto group_state = GroupState(); // empty group auto inj_controls = Well::InjectionControls(0); - auto prod_controls = Well::ProductionControls(0); + auto prod_controls = Well::ProductionControls(0); const bool converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger, /*fixed_control*/true, /*fixed_status*/ true); this->wellStatus_ = well_status_orig; return converged; - } + } template bool @@ -1430,67 +1428,39 @@ namespace Opm template bool WellInterface:: - wellUnderZeroRateTargetVersion(const Simulator& simulator, - const WellState& well_state, - DeferredLogger& deferred_logger) const + wellUnderZeroRateTarget(const Simulator& simulator, + const WellState& well_state, + DeferredLogger& deferred_logger) const { - // Extended version of WellInterfaceGeneric::wellUnderZeroRateTarget that also checks group controls + // Check if well is under zero rate control, either directly or from group const auto& ws = well_state.well(this->index_of_well_); - const auto& summaryState = simulator.vanguard().summaryState(); const bool isGroupControlled = (this->isInjector() && ws.injection_cmode == Well::InjectorCMode::GRUP) || (this->isProducer() && ws.production_cmode == Well::ProducerCMode::GRUP); if (!isGroupControlled) { - // well is not under group control, check "light-weight" version - return this->wellUnderZeroRateTarget(summaryState, well_state); + // well is not under group control, check "individual" version + const auto& summaryState = simulator.vanguard().summaryState(); + return this->wellUnderZeroRateTargetIndividual(summaryState, well_state); } else { - const auto& well = this->well_ecl_; - const auto& schedule = simulator.vanguard().schedule(); + const auto& summaryState = simulator.vanguard().summaryState(); const auto& group_state = simulator.problem().wellModel().groupState(); - const auto& group = schedule.getGroup(well.groupName(), this->currentStep()); - const Scalar efficiencyFactor = well.getEfficiencyFactor(); - if (this->isInjector()) { - // Check injector under group control - const auto& controls = well.injectionControls(summaryState); - std::optional target = this->getGroupInjectionTargetRate(group, - well_state, - group_state, - schedule, - summaryState, - controls.injector_type, - efficiencyFactor, - deferred_logger); - if (target.has_value()) { - return target.value() == 0.0; - } else { - return false; - } - } else { - // Check producer under group control - Scalar scale = this->getGroupProductionTargetRate(group, - well_state, - group_state, - schedule, - summaryState, - efficiencyFactor, - deferred_logger); - return scale == 0.0; - } + const auto& schedule = simulator.vanguard().schedule(); + return this->wellUnderZeroRateTargetGroup(summaryState, schedule, well_state, group_state, deferred_logger); } } template bool WellInterface:: - stoppedOrZeroRateTargetVersion(const Simulator& simulator, - const WellState& well_state, - DeferredLogger& deferred_logger) const + stoppedOrZeroRateTarget(const Simulator& simulator, + const WellState& well_state, + DeferredLogger& deferred_logger) const { - // Extended version of WellInterfaceGeneric::stopppedOrZeroRateTarget that also checks group controls - return (this->wellIsStopped() || wellUnderZeroRateTargetVersion(simulator, - well_state, - deferred_logger)); + // Check if well is stopped or under zero rate control, either directly or from group + return (this->wellIsStopped() || wellUnderZeroRateTarget(simulator, + well_state, + deferred_logger)); } - + template std::vector::Scalar> WellInterface::