From b292f39a0ce82c38f2899ef75103e7f6058ef62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20H=C3=A6gland?= Date: Wed, 8 Apr 2026 11:10:16 +0200 Subject: [PATCH] Populate satellite data for RC summary output Add updateScheduleSatelliteData() to ReservoirCouplingMaster and ReservoirCouplingMasterReportStep. Called from EclWriter::evalSummaryState() before summary evaluation. This populates the Schedule's GSatProd and GroupSatelliteInjection with slave production/injection rates received via MPI. The existing satellite_rate machinery in opm-common's Summary.cpp then automatically computes all rate-based summary vectors (FOPR, GOPR, FGOR, FWCT, etc.) correctly for the master model. Requires the companion opm-common PR that adds Schedule::updateSatelliteProduction/Injection(). --- opm/simulators/flow/EclWriter.hpp | 37 ++++++++++++++- .../flow/rescoup/ReservoirCouplingMaster.cpp | 9 ++++ .../flow/rescoup/ReservoirCouplingMaster.hpp | 7 +++ .../ReservoirCouplingMasterReportStep.cpp | 47 +++++++++++++++++++ .../ReservoirCouplingMasterReportStep.hpp | 11 +++++ 5 files changed, 110 insertions(+), 1 deletion(-) diff --git a/opm/simulators/flow/EclWriter.hpp b/opm/simulators/flow/EclWriter.hpp index aad5df7104..9719851e68 100644 --- a/opm/simulators/flow/EclWriter.hpp +++ b/opm/simulators/flow/EclWriter.hpp @@ -53,6 +53,11 @@ #include #include +#include +#ifdef RESERVOIR_COUPLING_ENABLED +#include +#endif + #include #include @@ -128,7 +133,7 @@ class EclWriter : public EclGenericWriter typedef Dune::MultipleCodimMultipleGeomTypeMapper< GridView > VertexMapper; - enum { enableEnergy = getPropValue() == EnergyModules::FullyImplicitThermal || + enum { enableEnergy = getPropValue() == EnergyModules::FullyImplicitThermal || getPropValue() == EnergyModules::SequentialImplicitThermal }; enum { enableMech = getPropValue() }; enum { enableSolvent = getPropValue() }; @@ -320,6 +325,13 @@ public: miscSummaryData["MSUMNEWT"] = this->simulation_report_.success.total_newton_iterations; } + // For reservoir coupling master: populate satellite production/injection + // data from slave groups so that summary.eval() picks up the rates through + // the existing GSatProd / satellite_rate machinery in opm-common's Summary.cpp. + // This must be done before evalSummary() so the rates are available when + // summary vectors are evaluated. + this->updateReservoirCouplingSatelliteData_(); + { OPM_TIMEBLOCK(evalSummary); @@ -752,6 +764,29 @@ private: const Schedule& schedule() const { return simulator_.vanguard().schedule(); } + /// Populate satellite production/injection data from reservoir coupling + /// slave groups into the Schedule's GSatProd / GroupSatelliteInjection + /// structures. Delegates to ReservoirCouplingMaster::updateScheduleSatelliteData(). + void updateReservoirCouplingSatelliteData_() + { +#ifdef RESERVOIR_COUPLING_ENABLED + // Guard: only BlackoilWellModel has reservoir coupling support. + // CompWellModel (compositional) does not, so we use if constexpr + // to avoid compilation errors when EclWriter is instantiated with + // a compositional TypeTag. + using WellModelType = std::remove_cvref_t< + decltype(simulator_.problem().wellModel())>; + if constexpr (requires(WellModelType& wm) { wm.isReservoirCouplingMaster(); }) { + auto& wellModel = simulator_.problem().wellModel(); + if (!wellModel.isReservoirCouplingMaster()) { + return; + } + wellModel.reservoirCouplingMaster().updateScheduleSatelliteData( + simulator_.vanguard().schedule(), simulator_.episodeIndex()); + } +#endif + } + void prepareLocalCellData(const bool isSubStep, const int reportStepNum) { diff --git a/opm/simulators/flow/rescoup/ReservoirCouplingMaster.cpp b/opm/simulators/flow/rescoup/ReservoirCouplingMaster.cpp index 3a1df5c170..ab1eb4a368 100644 --- a/opm/simulators/flow/rescoup/ReservoirCouplingMaster.cpp +++ b/opm/simulators/flow/rescoup/ReservoirCouplingMaster.cpp @@ -497,6 +497,15 @@ updateMasterGroupNameOrderMap( this->master_group_name_order_[slave_name] = master_group_name_map; } +template +void +ReservoirCouplingMaster:: +updateScheduleSatelliteData(Schedule& schedule, const int report_step_idx) +{ + assert(this->report_step_data_); + this->report_step_data_->updateScheduleSatelliteData(schedule, report_step_idx); +} + // ------------------ // Private methods // ------------------ diff --git a/opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp b/opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp index c91375899b..ca82d127d7 100644 --- a/opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp +++ b/opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp @@ -154,6 +154,13 @@ public: this->logger_.setDeferredLogger(deferred_logger); } void setFirstSubstepOfSyncTimestep(bool value); + + /// @brief Update the Schedule's satellite production/injection data from + /// slave group rates. + /// @details Called from EclWriter::evalSummaryState() at the end of each + /// sync step. Delegates to ReservoirCouplingMasterReportStep. See its + /// updateScheduleSatelliteData() for details. + void updateScheduleSatelliteData(Schedule& schedule, int report_step_idx); // These are currently only used for unit testing void setSlaveActivationDate(int index, double date) { this->slave_activation_dates_[index] = date; } void setSlaveNextReportTimeOffset(int index, double offset); diff --git a/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.cpp b/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.cpp index bcc64a7447..4e1b456565 100644 --- a/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.cpp +++ b/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.cpp @@ -320,6 +320,53 @@ setReportStepIdx(int report_step_idx) this->report_step_idx_ = report_step_idx; } +template +void +ReservoirCouplingMasterReportStep:: +updateScheduleSatelliteData(Schedule& schedule, const int report_step_idx) +{ + using RcPhase = ReservoirCoupling::Phase; + using RateKind = ReservoirCoupling::RateKind; + + const auto numSlaves = this->numSlaves(); + for (unsigned int i = 0; i < numSlaves; ++i) { + const auto& masterGroupNames = this->getMasterGroupNamesForSlave(i); + for (const auto& groupName : masterGroupNames) { + // GSatProd expects positive production rates. The rates from + // getMasterGroupRate_(ProductionSurface) originate from the slave's + // GuideRate::RateVector which stores positive values for production + // (guide rate convention, not the well-state sign convention). + const double oilRate = this->getMasterGroupRate_( + groupName, RcPhase::Oil, RateKind::ProductionSurface); + const double gasRate = this->getMasterGroupRate_( + groupName, RcPhase::Gas, RateKind::ProductionSurface); + const double waterRate = this->getMasterGroupRate_( + groupName, RcPhase::Water, RateKind::ProductionSurface); + const double resvRate = + this->getMasterGroupRate_(groupName, RcPhase::Oil, RateKind::ProductionReservoir) + + this->getMasterGroupRate_(groupName, RcPhase::Gas, RateKind::ProductionReservoir) + + this->getMasterGroupRate_(groupName, RcPhase::Water, RateKind::ProductionReservoir); + + schedule.updateSatelliteProduction( + report_step_idx, groupName, oilRate, gasRate, waterRate, resvRate); + + // Injection rates: positive values match what GSatInje expects. + for (const auto phase : {RcPhase::Oil, RcPhase::Gas, RcPhase::Water}) { + const double surfRate = this->getMasterGroupRate_( + groupName, phase, RateKind::InjectionSurface); + const double resRate = this->getMasterGroupRate_( + groupName, phase, RateKind::InjectionReservoir); + + if (surfRate != 0.0 || resRate != 0.0) { + const auto opmPhase = convertToOpmPhase(phase); + schedule.updateSatelliteInjection( + report_step_idx, groupName, opmPhase, surfRate, resRate); + } + } + } + } +} + // ------------------ // Private methods diff --git a/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.hpp b/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.hpp index 5fd5475f10..6be96bd951 100644 --- a/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.hpp +++ b/opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.hpp @@ -221,6 +221,17 @@ public: /// being processed. void setReportStepIdx(int report_step_idx); + /// @brief Update the Schedule's satellite production/injection data from + /// slave group rates received via MPI. + /// @details Populates the Schedule's GSatProd and GroupSatelliteInjection + /// for each master group so that opm-common's Summary.cpp satellite_rate + /// machinery computes all rate-based summary vectors correctly for all + /// master groups. + /// @param schedule Non-const reference to the Schedule (needed for + /// updateSatelliteProduction/Injection) + /// @param report_step_idx 0-based report step index + void updateScheduleSatelliteData(Schedule& schedule, int report_step_idx); + /// @brief Check if a specific slave process has been activated /// @param index Index of the slave process /// @return true if the slave is activated, false otherwise