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().
This commit is contained in:
Håkon Hægland committed 2026-04-10 10:40:05 +02:00
1 parent d01d75ce09
commit b292f39a0c
5 files changed
+110 -1

No files matched your search

+36 -1
View File
@@ -53,6 +53,11 @@
#include <opm/simulators/utils/ParallelRestart.hpp>
#include <opm/simulators/utils/ParallelSerialization.hpp>
#include <opm/simulators/flow/rescoup/ReservoirCouplingEnabled.hpp>
#ifdef RESERVOIR_COUPLING_ENABLED
#include <opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp>
#endif
#include <boost/date_time/posix_time/posix_time.hpp>
#include <limits>
@@ -128,7 +133,7 @@ class EclWriter : public EclGenericWriter<GetPropType<TypeTag, Properties::Grid>
typedef Dune::MultipleCodimMultipleGeomTypeMapper< GridView > VertexMapper;
enum { enableEnergy = getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal ||
enum { enableEnergy = getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal ||
getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::SequentialImplicitThermal };
enum { enableMech = getPropValue<TypeTag, Properties::EnableMech>() };
enum { enableSolvent = getPropValue<TypeTag, Properties::EnableSolvent>() };
@@ -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)
{
@@ -497,6 +497,15 @@ updateMasterGroupNameOrderMap(
this->master_group_name_order_[slave_name] = master_group_name_map;
}
template <class Scalar>
void
ReservoirCouplingMaster<Scalar>::
updateScheduleSatelliteData(Schedule& schedule, const int report_step_idx)
{
assert(this->report_step_data_);
this->report_step_data_->updateScheduleSatelliteData(schedule, report_step_idx);
}
// ------------------
// Private methods
// ------------------
@@ -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);
@@ -320,6 +320,53 @@ setReportStepIdx(int report_step_idx)
this->report_step_idx_ = report_step_idx;
}
template <class Scalar>
void
ReservoirCouplingMasterReportStep<Scalar>::
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
@@ -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