From 0240922cf46f870f68ba8a1f2c6cfcbe193f3ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Tue, 29 Oct 2024 13:52:47 +0100 Subject: [PATCH] Handle WELPI from ACTIONX Separately From Constraints This commit switches to using the 'welpi_wells' information from the SimulatorUpdate structure as a basis to decide the wells for which it is safe/sufficient to update only the CTFs in response to an ACTIONX block running WELPI. To this end, we split the actions of the existing member function updateEclWells() into two parts, 1. updateEclWellsConstraints() 2. updateEclWellsCTFFromAction() in which the first handles well status and well control updates while the second deals with CTF updates in response to WELPI. We do not run the second part if the well structure has changed--e.g., due to COMPDAT or WELOPEN--since the update loop depends on a static connection topology. We add a new member function wellUpdateLoop() which will traverse a sequence of well names and invoke a loop body on those wells which exist in wells_ecl_. This collects common operations needed for both the constraints and the CTF updates. --- .../wells/BlackoilWellModelGeneric.cpp | 95 ++++++++++++++----- .../wells/BlackoilWellModelGeneric.hpp | 10 ++ 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 3071849c6..44a441050 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -730,23 +731,82 @@ updateEclWells(const int timeStepIdx, const SimulatorUpdate& sim_update, const SummaryState& st) { - for (const auto& wname : sim_update.affected_wells) { - auto well_iter = std::find_if(this->wells_ecl_.begin(), this->wells_ecl_.end(), - [&wname] (const auto& well) -> bool - { - return well.name() == wname; - }); + this->updateEclWellsConstraints(timeStepIdx, sim_update, st); + + if (! sim_update.well_structure_changed && + ! this->wellStructureChangedDynamically_) + { + this->updateEclWellsCTFFromAction(timeStepIdx, sim_update); + } + + if (sim_update.well_structure_changed) { + // Note: Record change if this update triggered a well structure + // change. Otherwise, we risk setting ChangedDynamically_ to false + // if a subsequent action at the same time step does *not* change + // the well topology. + this->wellStructureChangedDynamically_ = true; + } +} + +template +template +void BlackoilWellModelGeneric:: +wellUpdateLoop(Iter first, Iter last, const int timeStepIdx, Body&& body) +{ + std::for_each(first, last, + [this, timeStepIdx, + loopBody = std::forward(body)] + (const auto& wname) + { + auto well_iter = std::find_if(this->wells_ecl_.begin(), + this->wells_ecl_.end(), + [&wname](const auto& well) + { return well.name() == wname; }); if (well_iter == this->wells_ecl_.end()) { - continue; + return; } - const auto well_index = std::distance(this->wells_ecl_.begin(), well_iter); + const auto wellIdx = + std::distance(this->wells_ecl_.begin(), well_iter); - const auto& well = this->wells_ecl_[well_index] = + const auto& well = this->wells_ecl_[wellIdx] = this->schedule_.getWell(wname, timeStepIdx); - auto& pd = this->well_perf_data_[well_index]; + loopBody(wellIdx, well); + }); +} + +template +void BlackoilWellModelGeneric:: +updateEclWellsConstraints(const int timeStepIdx, + const SimulatorUpdate& sim_update, + const SummaryState& st) +{ + this->wellUpdateLoop(sim_update.affected_wells.begin(), + sim_update.affected_wells.end(), + timeStepIdx, + [this, &st] + (const auto wellIdx, const auto& well) + { + auto& ws = this->wellState().well(wellIdx); + + ws.updateStatus(well.getStatus()); + ws.update_targets(well, st); + }); +} + +template +void BlackoilWellModelGeneric:: +updateEclWellsCTFFromAction(const int timeStepIdx, + const SimulatorUpdate& sim_update) +{ + this->wellUpdateLoop(sim_update.welpi_wells.begin(), + sim_update.welpi_wells.end(), + timeStepIdx, + [this](const auto wellIdx, const auto& well) + { + auto& pd = this->well_perf_data_[wellIdx]; { auto pdIter = pd.begin(); @@ -759,18 +819,9 @@ updateEclWells(const int timeStepIdx, } } - { - auto& ws = this->wellState().well(well_index); - - ws.updateStatus(well.getStatus()); - ws.reset_connection_factors(pd); - ws.update_targets(well, st); - } - - this->prod_index_calc_[well_index].reInit(well); - } - - this->wellStructureChangedDynamically_ = sim_update.well_structure_changed; + this->wellState().well(wellIdx).reset_connection_factors(pd); + this->prod_index_calc_[wellIdx].reInit(well); + }); } template diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index 8d8a53701..324d63939 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -601,6 +601,16 @@ protected: private: WellInterfaceGeneric* getGenWell(const std::string& well_name); + + template + void wellUpdateLoop(Iter first, Iter last, const int timeStepIdx, Body&& body); + + void updateEclWellsConstraints(const int timeStepIdx, + const SimulatorUpdate& sim_update, + const SummaryState& st); + + void updateEclWellsCTFFromAction(const int timeStepIdx, + const SimulatorUpdate& sim_update); };