From b4ee45513362f2aaaa45c3ed4aea165a592ae24e Mon Sep 17 00:00:00 2001 From: Vegard Kippe Date: Tue, 5 Nov 2024 12:47:11 +0100 Subject: [PATCH 1/2] Limit communication of group state to actually changed vectors + rename 'currentWellRates' in WellState to reflect that these are actually only used for guide rates --- .../wells/BlackoilWellModelGeneric.cpp | 112 +++++++++--------- .../wells/BlackoilWellModelGuideRates.cpp | 6 +- .../wells/BlackoilWellModel_impl.hpp | 1 + opm/simulators/wells/FractionCalculator.cpp | 6 +- opm/simulators/wells/FractionCalculator.hpp | 2 +- opm/simulators/wells/GroupState.hpp | 30 +++-- opm/simulators/wells/WellGroupHelpers.cpp | 26 ++-- opm/simulators/wells/WellGroupHelpers.hpp | 8 +- opm/simulators/wells/WellState.cpp | 24 ++-- opm/simulators/wells/WellState.hpp | 27 ++--- 10 files changed, 129 insertions(+), 113 deletions(-) diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index 44a44105053..b81b704be6d 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -271,7 +271,7 @@ prepareDeserialize(int report_step, const std::size_t numCells, bool handle_ms_w this->well_perf_data_, this->summaryState_); } - this->wellState().clearWellRates(); + this->wellState().clearWellRatesForGuideRate(); this->commitWGState(); this->updateNupcolWGState(); } @@ -1188,12 +1188,65 @@ updateAndCommunicateGroupData(const int reportStepIdx, // before we copy to well_state_nupcol_. this->wellState().updateGlobalIsGrup(comm_); - if (iterationIdx < nupcol) { + const bool update_nupcol = iterationIdx < nupcol; + if (update_nupcol) { this->updateNupcolWGState(); } - auto& well_state = this->wellState(); - const auto& well_state_nupcol = this->nupcolWellState(); + + if (iterationIdx == 0) { + // We use the rates from the previous time-step to reduce oscillations + WellGroupHelpers::updateWellRatesForGuideRate(fieldGroup, + schedule(), + reportStepIdx, + this->prevWellState(), + well_state); + + // Set ALQ for off-process wells to zero + for (const auto& wname : schedule().wellNames(reportStepIdx)) { + const bool is_producer = schedule().getWell(wname, reportStepIdx).isProducer(); + const bool not_on_this_process = !well_state.has(wname); + if (is_producer && not_on_this_process) { + well_state.setALQ(wname, 0.0); + } + } + + well_state.communicateWellRatesForGuideRate(comm_); + } + + if (update_nupcol) { + WellGroupHelpers::updateREINForGroups(fieldGroup, + schedule(), + reportStepIdx, + phase_usage_, + summaryState_, + well_state, + this->groupState(), + comm_.rank() == 0); + WellGroupHelpers::updateVREPForGroups(fieldGroup, + schedule(), + reportStepIdx, + well_state, + this->groupState()); + + WellGroupHelpers::updateReservoirRatesInjectionGroups(fieldGroup, + schedule(), + reportStepIdx, + well_state, + this->groupState()); + WellGroupHelpers::updateSurfaceRatesInjectionGroups(fieldGroup, + schedule(), + reportStepIdx, + well_state, + this->groupState()); + + WellGroupHelpers::updateGroupProductionRates(fieldGroup, + schedule(), + reportStepIdx, + well_state, + this->groupState()); + } + // the group target reduction rates needs to be update since wells may have switched to/from GRUP control // The group target reduction does not honor NUPCOL. std::vector groupTargetReduction(numPhases(), 0.0); @@ -1217,55 +1270,8 @@ updateAndCommunicateGroupData(const int reportStepIdx, this->groupState(), groupTargetReductionInj); - WellGroupHelpers::updateREINForGroups(fieldGroup, - schedule(), - reportStepIdx, - phase_usage_, - summaryState_, - well_state_nupcol, - this->groupState(), - comm_.rank() == 0); - WellGroupHelpers::updateVREPForGroups(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState()); - - WellGroupHelpers::updateReservoirRatesInjectionGroups(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState()); - WellGroupHelpers::updateSurfaceRatesInjectionGroups(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState()); - - WellGroupHelpers::updateGroupProductionRates(fieldGroup, - schedule(), - reportStepIdx, - well_state_nupcol, - this->groupState()); - - // We use the rates from the previous time-step to reduce oscillations - WellGroupHelpers::updateWellRates(fieldGroup, - schedule(), - reportStepIdx, - this->prevWellState(), - well_state); - - // Set ALQ for off-process wells to zero - for (const auto& wname : schedule().wellNames(reportStepIdx)) { - const bool is_producer = schedule().getWell(wname, reportStepIdx).isProducer(); - const bool not_on_this_process = !well_state.has(wname); - if (is_producer && not_on_this_process) { - well_state.setALQ(wname, 0.0); - } - } - - well_state.communicateGroupRates(comm_); - this->groupState().communicate_rates(comm_); + // well_state.communicateGroupRates(comm_); + this->groupState().communicate_rates(comm_, update_nupcol); } template diff --git a/opm/simulators/wells/BlackoilWellModelGuideRates.cpp b/opm/simulators/wells/BlackoilWellModelGuideRates.cpp index f597a13210d..4f8a18295aa 100644 --- a/opm/simulators/wells/BlackoilWellModelGuideRates.cpp +++ b/opm/simulators/wells/BlackoilWellModelGuideRates.cpp @@ -330,7 +330,7 @@ getGuideRateValues(const Well& well) const } const auto qs = WellGroupHelpers:: - getWellRateVector(wellModel_.wellState(), wellModel_.phaseUsage(), wname); + getWellGuideRateVector(wellModel_.wellState(), wellModel_.phaseUsage(), wname); this->getGuideRateValues(qs, well.isInjector(), wname, grval); @@ -358,7 +358,7 @@ getGuideRateValues(const Group& group) const } const auto qs = WellGroupHelpers:: - getProductionGroupRateVector(wellModel_.groupState(), wellModel_.phaseUsage(), gname); + getProductionGroupGuideRateVector(wellModel_.groupState(), wellModel_.phaseUsage(), gname); const auto is_inj = false; // This procedure only applies to G*PGR. this->getGuideRateValues(qs, is_inj, gname, grval); @@ -438,7 +438,7 @@ assignWellGuideRates(data::Wells& wsrpt, || RetrieveWellGuideRate{wellModel_.guideRate(), wname}; const auto qs = WellGroupHelpers:: - getWellRateVector(wellModel_.wellState(), wellModel_.phaseUsage(), wname); + getWellGuideRateVector(wellModel_.wellState(), wellModel_.phaseUsage(), wname); auto getGR = [this, &wname, &qs](const GuideRateModel::Target t) { diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 344e179fe7c..2fa1dcd35d0 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -1266,6 +1266,7 @@ namespace Opm { OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger, "updateWellControlsAndNetworkIteration() failed: ", this->terminal_output_, grid().comm()); + // guideRateUpdateIsNeeded - only required at start of time step? //update guide rates const int reportStepIdx = simulator_.episodeIndex(); if (alq_updated || BlackoilWellModelGuideRates(*this). diff --git a/opm/simulators/wells/FractionCalculator.cpp b/opm/simulators/wells/FractionCalculator.cpp index 9e7ae0ff344..78631930eb7 100644 --- a/opm/simulators/wells/FractionCalculator.cpp +++ b/opm/simulators/wells/FractionCalculator.cpp @@ -149,7 +149,7 @@ guideRate(const std::string& name, } else { if (groupControlledWells(name, always_included_child) > 0) { if (is_producer_ && guide_rate_->has(name)) { - return guide_rate_->get(name, target_, getGroupRateVector(name)); + return guide_rate_->get(name, target_, getGroupGuideRateVector(name)); } else if (!is_producer_ && guide_rate_->has(name, injection_phase_)) { return guide_rate_->get(name, injection_phase_); } else { @@ -183,10 +183,10 @@ groupControlledWells(const std::string& group_name, template GuideRate::RateVector FractionCalculator:: -getGroupRateVector(const std::string& group_name) +getGroupGuideRateVector(const std::string& group_name) { assert(is_producer_); - return WellGroupHelpers::getProductionGroupRateVector(this->group_state_, + return WellGroupHelpers::getProductionGroupGuideRateVector(this->group_state_, this->pu_, group_name); } diff --git a/opm/simulators/wells/FractionCalculator.hpp b/opm/simulators/wells/FractionCalculator.hpp index d421f5c3d13..dd472d4eb97 100644 --- a/opm/simulators/wells/FractionCalculator.hpp +++ b/opm/simulators/wells/FractionCalculator.hpp @@ -61,7 +61,7 @@ class FractionCalculator const std::string& always_included_child); int groupControlledWells(const std::string& group_name, const std::string& always_included_child); - GuideRate::RateVector getGroupRateVector(const std::string& group_name); + GuideRate::RateVector getGroupGuideRateVector(const std::string& group_name); const Schedule& schedule_; const WellState& well_state_; const GroupState& group_state_; diff --git a/opm/simulators/wells/GroupState.hpp b/opm/simulators/wells/GroupState.hpp index cc22cde8bf3..f01000c276a 100644 --- a/opm/simulators/wells/GroupState.hpp +++ b/opm/simulators/wells/GroupState.hpp @@ -102,7 +102,7 @@ class GroupState { GPMaint::State& gpmaint(const std::string& gname); template - void communicate_rates(const Comm& comm) + void communicate_rates(const Comm& comm, const bool update_nupcol) { // Note that injection_group_vrep_rates is handled separate from // the forAllGroupData() function, since it contains single doubles, @@ -119,12 +119,14 @@ class GroupState { auto forAllGroupData = [&](auto& func) { - iterateContainer(m_production_rates, func); iterateContainer(prod_red_rates, func); iterateContainer(inj_red_rates, func); - iterateContainer(inj_resv_rates, func); - iterateContainer(inj_rein_rates, func); - iterateContainer(inj_surface_rates, func); + if (update_nupcol) { + iterateContainer(m_production_rates, func); + iterateContainer(inj_resv_rates, func); + iterateContainer(inj_rein_rates, func); + iterateContainer(inj_surface_rates, func); + } }; // Compute the size of the data. @@ -133,7 +135,9 @@ class GroupState { sz += v.size(); }; forAllGroupData(computeSize); - sz += this->inj_vrep_rate.size(); + if (update_nupcol) { + sz += this->inj_vrep_rate.size(); + } // Make a vector and collect all data into it. std::vector data(sz); @@ -148,9 +152,12 @@ class GroupState { } }; forAllGroupData(collect); - for (const auto& x : this->inj_vrep_rate) { - data[pos++] = x.second; + if (update_nupcol) { + for (const auto& x : this->inj_vrep_rate) { + data[pos++] = x.second; + } } + if (pos != sz) throw std::logic_error("Internal size mismatch when collecting groupData"); @@ -165,9 +172,12 @@ class GroupState { } }; forAllGroupData(distribute); - for (auto& x : this->inj_vrep_rate) { - x.second = data[pos++]; + if (update_nupcol) { + for (auto& x : this->inj_vrep_rate) { + x.second = data[pos++]; + } } + if (pos != sz) throw std::logic_error("Internal size mismatch when distributing groupData"); } diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index 00f2e89eff5..187283e31f2 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -626,15 +626,15 @@ updateSurfaceRatesInjectionGroups(const Group& group, template void WellGroupHelpers:: -updateWellRates(const Group& group, +updateWellRatesForGuideRate(const Group& group, const Schedule& schedule, const int reportStepIdx, - const WellState& wellStateNupcol, + const WellState& guiderateWellState, WellState& wellState) { for (const std::string& groupName : group.groups()) { const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx); - updateWellRates(groupTmp, schedule, reportStepIdx, wellStateNupcol, wellState); + updateWellRatesForGuideRate(groupTmp, schedule, reportStepIdx, guiderateWellState, wellState); } const int np = wellState.numPhases(); for (const std::string& wellName : group.wells()) { @@ -643,16 +643,16 @@ updateWellRates(const Group& group, if (well_index.has_value()) { // the well is found on this node const auto& wellTmp = schedule.getWell(wellName, reportStepIdx); int sign = 1; - // production wellRates are negative. The users of currentWellRates uses the convention in - // opm-common that production and injection rates are positive. + // production wellRates are negative. The users of wellRatesForGuideRate use + // the convention in opm-common that production and injection rates are positive. if (!wellTmp.isInjector()) sign = -1; - const auto& ws = wellStateNupcol.well(well_index.value()); + const auto& ws = guiderateWellState.well(well_index.value()); for (int phase = 0; phase < np; ++phase) { rates[phase] = sign * ws.surface_rates[phase]; } } - wellState.setCurrentWellRates(wellName, rates); + wellState.setWellRatesForGuideRate(wellName, rates); } } @@ -957,17 +957,17 @@ computeNetworkPressures(const Network::ExtNetwork& network, template GuideRate::RateVector WellGroupHelpers:: -getWellRateVector(const WellState& well_state, +getWellGuideRateVector(const WellState& well_state, const PhaseUsage& pu, const std::string& name) { - return getGuideRateVector(well_state.currentWellRates(name), pu); + return getGuideRateVector(well_state.getWellRatesForGuideRate(name), pu); } template GuideRate::RateVector WellGroupHelpers:: -getProductionGroupRateVector(const GroupState& group_state, +getProductionGroupGuideRateVector(const GroupState& group_state, const PhaseUsage& pu, const std::string& group_name) { @@ -987,14 +987,14 @@ getGuideRate(const std::string& name, { if (schedule.hasWell(name, reportStepIdx)) { if (guideRate->has(name) || guideRate->hasPotentials(name)) { - return guideRate->get(name, target, getWellRateVector(wellState, pu, name)); + return guideRate->get(name, target, getWellGuideRateVector(wellState, pu, name)); } else { return 0.0; } } if (guideRate->has(name)) { - return guideRate->get(name, target, getProductionGroupRateVector(group_state, pu, name)); + return guideRate->get(name, target, getProductionGroupGuideRateVector(group_state, pu, name)); } Scalar totalGuideRate = 0.0; @@ -1076,7 +1076,7 @@ getGuideRateInj(const std::string& name, if (!wellState.isInjectionGrup(wellName)) continue; - totalGuideRate += guideRate->get(wellName, target, getWellRateVector(wellState, pu, wellName)); + totalGuideRate += guideRate->get(wellName, target, getWellGuideRateVector(wellState, pu, wellName)); } return totalGuideRate; } diff --git a/opm/simulators/wells/WellGroupHelpers.hpp b/opm/simulators/wells/WellGroupHelpers.hpp index 70754640bef..8ca5612e5b6 100644 --- a/opm/simulators/wells/WellGroupHelpers.hpp +++ b/opm/simulators/wells/WellGroupHelpers.hpp @@ -168,10 +168,10 @@ class WellGroupHelpers const WellState& wellState, GroupState& group_state); - static void updateWellRates(const Group& group, + static void updateWellRatesForGuideRate(const Group& group, const Schedule& schedule, const int reportStepIdx, - const WellState& wellStateNupcol, + const WellState& guiderateWellState, WellState& wellState); static void updateGroupProductionRates(const Group& group, @@ -207,12 +207,12 @@ class WellGroupHelpers const int report_time_step); static GuideRate::RateVector - getWellRateVector(const WellState& well_state, + getWellGuideRateVector(const WellState& well_state, const PhaseUsage& pu, const std::string& name); static GuideRate::RateVector - getProductionGroupRateVector(const GroupState& group_state, + getProductionGroupGuideRateVector(const GroupState& group_state, const PhaseUsage& pu, const std::string& group_name); diff --git a/opm/simulators/wells/WellState.cpp b/opm/simulators/wells/WellState.cpp index 7484aa9b5c8..6a42b1016e5 100644 --- a/opm/simulators/wells/WellState.cpp +++ b/opm/simulators/wells/WellState.cpp @@ -141,7 +141,7 @@ serializationTestObject(const ParallelWellInfo& pinfo) { WellState result(PhaseUsage{}); result.alq_state = ALQState::serializationTestObject(); - result.well_rates = {{"test2", {true, {1.0}}}, {"test3", {false, {2.0}}}}; + result.well_rates_for_guiderate = {{"test2", {true, {1.0}}}, {"test3", {false, {2.0}}}}; result.wells_.add("test4", SingleWellState::serializationTestObject(pinfo)); return result; @@ -272,14 +272,14 @@ void WellState::init(const std::vector& cellPressures, this->global_well_info = std::make_optional(schedule, report_step, wells_ecl); - well_rates.clear(); + well_rates_for_guiderate.clear(); for (const auto& wname : schedule.wellNames(report_step)) { - well_rates.insert({wname, std::make_pair(false, std::vector(this->numPhases()))}); + well_rates_for_guiderate.insert({wname, std::make_pair(false, std::vector(this->numPhases()))}); } for (const auto& winfo: parallel_well_info) { - well_rates[winfo.get().name()].first = winfo.get().isOwner(); + well_rates_for_guiderate[winfo.get().name()].first = winfo.get().isOwner(); } const int nw = wells_ecl.size(); @@ -447,11 +447,11 @@ void WellState::resize(const std::vector& wells_ecl, template const std::vector& -WellState::currentWellRates(const std::string& wellName) const +WellState::getWellRatesForGuideRate(const std::string& wellName) const { - auto it = well_rates.find(wellName); + auto it = well_rates_for_guiderate.find(wellName); - if (it == well_rates.end()) + if (it == well_rates_for_guiderate.end()) OPM_THROW(std::logic_error, "Could not find any rates for well " + wellName); @@ -866,11 +866,11 @@ void WellState::updateStatus(int well_index, WellStatus status) } template -void WellState::communicateGroupRates(const Parallel::Communication& comm) +void WellState::communicateWellRatesForGuideRate(const Parallel::Communication& comm) { // Compute the size of the data. std::size_t sz = 0; - for (const auto& [_, owner_rates] : this->well_rates) { + for (const auto& [_, owner_rates] : this->well_rates_for_guiderate) { (void)_; const auto& [__, rates] = owner_rates; (void)__; @@ -881,7 +881,7 @@ void WellState::communicateGroupRates(const Parallel::Communication& com // Make a vector and collect all data into it. std::vector data(sz); std::size_t pos = 0; - for (const auto& [_, owner_rates] : this->well_rates) { + for (const auto& [_, owner_rates] : this->well_rates_for_guiderate) { (void)_; const auto& [owner, rates] = owner_rates; for (const auto& value : rates) { @@ -900,7 +900,7 @@ void WellState::communicateGroupRates(const Parallel::Communication& com comm.sum(data.data(), data.size()); pos = 0; - for (auto& [_, owner_rates] : this->well_rates) { + for (auto& [_, owner_rates] : this->well_rates_for_guiderate) { (void)_; auto& [__, rates] = owner_rates; (void)__; @@ -1049,7 +1049,7 @@ template bool WellState::operator==(const WellState& rhs) const { return this->alq_state == rhs.alq_state && - this->well_rates == rhs.well_rates && + this->well_rates_for_guiderate == rhs.well_rates_for_guiderate && this->wells_ == rhs.wells_; } diff --git a/opm/simulators/wells/WellState.hpp b/opm/simulators/wells/WellState.hpp index 7eb9b7e3399..0236bbf5e68 100644 --- a/opm/simulators/wells/WellState.hpp +++ b/opm/simulators/wells/WellState.hpp @@ -114,24 +114,24 @@ class WellState const std::vector>>& well_perf_data, const SummaryState& summary_state); - void setCurrentWellRates(const std::string& wellName, + void setWellRatesForGuideRate(const std::string& wellName, const std::vector& new_rates) { - auto& [owner, rates] = this->well_rates.at(wellName); + auto& [owner, rates] = this->well_rates_for_guiderate.at(wellName); if (owner) rates = new_rates; } - const std::vector& currentWellRates(const std::string& wellName) const; + const std::vector& getWellRatesForGuideRate(const std::string& wellName) const; - bool hasWellRates(const std::string& wellName) const + bool hasWellRatesForGuideRate(const std::string& wellName) const { - return this->well_rates.find(wellName) != this->well_rates.end(); + return this->well_rates_for_guiderate.find(wellName) != this->well_rates_for_guiderate.end(); } - void clearWellRates() + void clearWellRatesForGuideRate() { - this->well_rates.clear(); + this->well_rates_for_guiderate.clear(); } void gatherVectorsOnRoot(const std::vector& from_connections, @@ -159,7 +159,7 @@ class WellState std::vector& segment_rates); - void communicateGroupRates(const Parallel::Communication& comm); + void communicateWellRatesForGuideRate(const Parallel::Communication& comm); void updateGlobalIsGrup(const Parallel::Communication& comm); @@ -335,7 +335,7 @@ class WellState void serializeOp(Serializer& serializer) { serializer(alq_state); - serializer(well_rates); + serializer(well_rates_for_guiderate); if (serializer.isSerializing()) { serializer(wells_.size()); } else { @@ -358,7 +358,7 @@ class WellState // well might appear in the WellContainer on different processes. WellContainer> wells_; - // The members alq_state, global_well_info and well_rates are map like + // The members alq_state, global_well_info and well_rates_for_guiderate are map like // structures which will have entries for *all* the wells in the system. // Use of std::optional<> here is a technical crutch, the @@ -367,10 +367,9 @@ class WellState std::optional global_well_info; ALQState alq_state; - // The well_rates variable is defined for all wells on all processors. The - // bool in the value pair is whether the current process owns the well or - // not. - std::map>> well_rates; + // The well_rates_for_guiderate variable is defined for all wells on all processors. The + // bool in the value pair is whether the current process owns the well or not. + std::map>> well_rates_for_guiderate; data::Segment reportSegmentResults(const int well_id, From cd6a22b5a5e986945105212d33fcc7d3f5e68fc6 Mon Sep 17 00:00:00 2001 From: Vegard Kippe Date: Tue, 5 Nov 2024 14:05:03 +0100 Subject: [PATCH 2/2] Update GroupState test --- tests/test_GroupState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_GroupState.cpp b/tests/test_GroupState.cpp index 7f0c1c64c6c..84f8b002c14 100644 --- a/tests/test_GroupState.cpp +++ b/tests/test_GroupState.cpp @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(GroupStateCreate) auto gs2 = gs; BOOST_CHECK(gs2 == gs); TestCommunicator comm; - gs.communicate_rates(comm); + gs.communicate_rates(comm, true); BOOST_CHECK(gs2 == gs); }