Skip to content

Commit d01d75c

Browse files
authored
Merge pull request #6978 from hakonhagland/fix_rate_comm
Reservoir coupling: Add end-of-sync-step slave-to-master data exchange
2 parents 5253f3b + ffa6352 commit d01d75c

10 files changed

+152
-0
lines changed

opm/simulators/flow/rescoup/ReservoirCouplingMaster.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,24 @@ isFirstSubstepOfSyncTimestep() const
172172
return this->report_step_data_->isFirstSubstepOfSyncTimestep();
173173
}
174174

175+
template <class Scalar>
176+
bool
177+
ReservoirCouplingMaster<Scalar>::
178+
needsSlaveDataReceive() const
179+
{
180+
assert(this->report_step_data_);
181+
return this->report_step_data_->needsSlaveDataReceive();
182+
}
183+
184+
template <class Scalar>
185+
void
186+
ReservoirCouplingMaster<Scalar>::
187+
setNeedsSlaveDataReceive(bool value)
188+
{
189+
assert(this->report_step_data_);
190+
this->report_step_data_->setNeedsSlaveDataReceive(value);
191+
}
192+
175193
template <class Scalar>
176194
bool
177195
ReservoirCouplingMaster<Scalar>::

opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ class ReservoirCouplingMaster {
105105
void initTimeStepping();
106106
bool isFirstSubstepOfSyncTimestep() const;
107107
bool isMasterGroup(const std::string &group_name) const;
108+
/// @brief Check if the master needs to receive production data from the slaves.
109+
/// @details This flag is used to control reservoir coupling synchronization of
110+
/// summary data sent from the slaves to the master process.
111+
/// The master blocks in timeStepSucceeded() until all slaves have sent
112+
/// their production data.
113+
/// @return true if the master needs to receive production data from the slaves, false if not
114+
bool needsSlaveDataReceive() const;
115+
/// @brief Set whether the master needs to receive production data from the slaves.
116+
/// @details See needsSlaveDataReceive() for details.
117+
/// @param value true if the master needs to receive production data from the slaves, false if not
118+
void setNeedsSlaveDataReceive(bool value);
108119
ReservoirCoupling::Logger& logger() { return this->logger_; }
109120
ReservoirCoupling::Logger& logger() const { return this->logger_; }
110121
void maybeActivate(int report_step);

opm/simulators/flow/rescoup/ReservoirCouplingMasterReportStep.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ class ReservoirCouplingMasterReportStep {
154154
/// @return true if this is the first substep of a "sync" timestep, false if not
155155
bool isFirstSubstepOfSyncTimestep() const { return is_first_substep_of_sync_timestep_; }
156156

157+
/// @brief Check if the master needs to receive slave data at the next
158+
/// timeStepSucceeded() call.
159+
/// @details The slave data is needed for correct summary output.
160+
bool needsSlaveDataReceive() const { return needs_slave_data_receive_; }
161+
162+
/// @brief Set/clear the flag for pending slave data receive.
163+
void setNeedsSlaveDataReceive(bool value) { needs_slave_data_receive_ = value; }
164+
157165
/// @brief Get the number of slave groups for a specific slave process
158166
/// @param index Index of the slave process
159167
/// @return Number of groups managed by the specified slave
@@ -247,6 +255,13 @@ class ReservoirCouplingMasterReportStep {
247255
/// Flag to track if this is the first substep within a "sync" timestep.
248256
/// Used to control reservoir coupling synchronization.
249257
bool is_first_substep_of_sync_timestep_{true};
258+
259+
/// Flag to indicate that the master needs to receive end-of-sync-step
260+
/// production data from slaves. Set at the start of each sync step,
261+
/// cleared after the receive. The receive happens in timeStepSucceeded()
262+
/// on the first converged substep, so that the data is available for
263+
/// evalSummaryState() and all subsequent substeps of the same sync step.
264+
bool needs_slave_data_receive_{false};
250265
};
251266
} // namespace Opm
252267
#endif // OPM_RESERVOIR_COUPLING_MASTER_REPORT_STEP_HPP

opm/simulators/flow/rescoup/ReservoirCouplingSlave.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ isFirstSubstepOfSyncTimestep() const
107107
return this->report_step_data_->isFirstSubstepOfSyncTimestep();
108108
}
109109

110+
template <class Scalar>
111+
bool
112+
ReservoirCouplingSlave<Scalar>::
113+
isLastSubstepOfSyncTimestep() const
114+
{
115+
assert(this->report_step_data_);
116+
return this->report_step_data_->isLastSubstepOfSyncTimestep();
117+
}
118+
110119
template <class Scalar>
111120
bool
112121
ReservoirCouplingSlave<Scalar>::
@@ -351,6 +360,15 @@ setFirstSubstepOfSyncTimestep(bool value)
351360
this->report_step_data_->setFirstSubstepOfSyncTimestep(value);
352361
}
353362

363+
template <class Scalar>
364+
void
365+
ReservoirCouplingSlave<Scalar>::
366+
setLastSubstepOfSyncTimestep(bool value)
367+
{
368+
assert(this->report_step_data_);
369+
this->report_step_data_->setLastSubstepOfSyncTimestep(value);
370+
}
371+
354372
// ------------------
355373
// Private methods
356374
// ------------------

opm/simulators/flow/rescoup/ReservoirCouplingSlave.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ class ReservoirCouplingSlave {
7070
bool hasMasterProductionTarget(const std::string& gname) const;
7171
void initTimeStepping();
7272
bool isFirstSubstepOfSyncTimestep() const;
73+
/// @brief Check if this is the last substep within a "sync" timestep.
74+
/// @details This flag is used to control reservoir coupling synchronization of
75+
/// summary data sent from the slave to the master process.
76+
/// The slave should send production data to the master at the end of
77+
/// its "sync" timestep, while master is waiting for it in timeStepSucceeded()
78+
/// of the first substep of the sync step.
79+
/// @return true if this is the last substep of a "sync" timestep, false if not
80+
bool isLastSubstepOfSyncTimestep() const;
7381
bool isSlaveGroup(const std::string& group_name) const;
7482
ReservoirCoupling::Logger& logger() { return this->logger_; }
7583
ReservoirCoupling::Logger& logger() const { return this->logger_; }
@@ -105,6 +113,10 @@ class ReservoirCouplingSlave {
105113
this->logger_.setDeferredLogger(deferred_logger);
106114
}
107115
void setFirstSubstepOfSyncTimestep(bool value);
116+
/// @brief Set whether this is the last substep within a "sync" timestep.
117+
/// @details See isLastSubstepOfSyncTimestep() for details.
118+
/// @param value true if this is the last substep of a "sync" timestep, false if not
119+
void setLastSubstepOfSyncTimestep(bool value);
108120
const std::string& slaveGroupIdxToGroupName(std::size_t group_idx) const {
109121
return this->slave_group_order_.at(group_idx);
110122
}

opm/simulators/flow/rescoup/ReservoirCouplingSlaveReportStep.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ class ReservoirCouplingSlaveReportStep {
9898
/// @return true if this is the first substep of a "sync" timestep, false if not
9999
bool isFirstSubstepOfSyncTimestep() const { return is_first_substep_of_sync_timestep_; }
100100

101+
/// @brief Check if this is the last substep within a "sync" timestep.
102+
/// @details This flag is used to control reservoir coupling synchronization of
103+
/// summary data sent from the slave to the master process.
104+
/// The slave should send production data to the master at the end of
105+
/// its "sync" timestep, while master is waiting for it in timeStepSucceeded()
106+
/// of the first substep of the sync step.
107+
/// @return true if this is the last substep of a "sync" timestep, false if not
108+
bool isLastSubstepOfSyncTimestep() const { return is_last_substep_of_sync_timestep_; }
109+
101110
/// @brief Get the logger for reservoir coupling operations
102111
/// @return Reference to the logger object for this coupling session
103112
ReservoirCoupling::Logger& logger() const { return this->slave_.logger(); }
@@ -162,6 +171,11 @@ class ReservoirCouplingSlaveReportStep {
162171
/// @param value true at start of sync timestep, false after first runSubStep_() call
163172
void setFirstSubstepOfSyncTimestep(bool value) { is_first_substep_of_sync_timestep_ = value; }
164173

174+
/// @brief Set whether this is the last substep within a "sync" timestep.
175+
/// @details See isLastSubstepOfSyncTimestep() for details.
176+
/// @param value true if this is the last substep of a "sync" timestep, false if not
177+
void setLastSubstepOfSyncTimestep(bool value) { is_last_substep_of_sync_timestep_ = value; }
178+
165179
/// @brief Get the name of this slave process
166180
/// @return Reference to the name string for this slave
167181
const std::string& slaveName() const { return this->slave_.getSlaveName(); }
@@ -215,6 +229,10 @@ class ReservoirCouplingSlaveReportStep {
215229
// Flag to track if this is the first substep within a "sync" timestep.
216230
// Used to control reservoir coupling synchronization.
217231
bool is_first_substep_of_sync_timestep_{true};
232+
// Flag to track if this is the last substep within a "sync" timestep.
233+
// Used to control reservoir coupling synchronization of summary data sent from
234+
// the slave to the master process.
235+
bool is_last_substep_of_sync_timestep_{false};
218236

219237
// Master-imposed targets and corresponding control modes, received from the master
220238
// process at the beginning of each sync timestep. Cleared and repopulated on every

opm/simulators/timestepping/AdaptiveTimeStepping.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ class AdaptiveTimeStepping
156156
double maybeRestrictTimeStepGrowth_(const double dt,
157157
double dt_estimate,
158158
const int restarts) const;
159+
void maybeUpdateLastSubstepOfSyncTimestep_(double dt);
159160
void maybeUpdateTuningAndTimeStep_();
160161
double maxGrowth_() const;
161162
double minTimeStepBeforeClosingWells_() const;

opm/simulators/timestepping/AdaptiveTimeStepping_impl.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,10 @@ runStepReservoirCouplingMaster_()
682682
// whether master-slave data exchange should occur in beginTimeStep() in the well model.
683683
// It will be cleared after the first runSubStep_() call.
684684
reservoirCouplingMaster_().setFirstSubstepOfSyncTimestep(true);
685+
// After the first master substep completes, timeStepSucceeded() will
686+
// block until slaves finish the sync step and send production data.
687+
// This ensures correct summary output for all subsequent substeps.
688+
reservoirCouplingMaster_().setNeedsSlaveDataReceive(true);
685689
SubStepIteration<Solver> substepIteration{*this, substep_timer, current_step_length, final_step};
686690
const auto sub_steps_report = substepIteration.run();
687691
report += sub_steps_report;
@@ -806,6 +810,7 @@ run()
806810
detail::logTimer(this->substep_timer_);
807811
}
808812

813+
maybeUpdateLastSubstepOfSyncTimestep_(dt); // Needed for reservoir coupling
809814
auto substep_report = runSubStep_();
810815
markFirstSubStepAsFinished_(); // Needed for reservoir coupling
811816

@@ -1154,6 +1159,28 @@ maybeRestrictTimeStepGrowth_(const double dt, double dt_estimate, const int rest
11541159
return dt_estimate;
11551160
}
11561161

1162+
1163+
template<class TypeTag>
1164+
template<class Solver>
1165+
void
1166+
AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1167+
maybeUpdateLastSubstepOfSyncTimestep_(double dt)
1168+
{
1169+
#ifdef RESERVOIR_COUPLING_ENABLED
1170+
// For reservoir coupling slaves: predict if this substep will complete
1171+
// the sync timestep. If so, timeStepSucceeded() will send production
1172+
// data to the master (which is blocking on receive after its first substep).
1173+
// This is used for summary data synchronization between slaves and master.
1174+
if (isReservoirCouplingSlave_()) {
1175+
const bool is_last = ReservoirCoupling::Seconds::compare_gt_or_eq(
1176+
this->substep_timer_.simulationTimeElapsed() + dt,
1177+
this->substep_timer_.totalTime()
1178+
);
1179+
reservoirCouplingSlave_().setLastSubstepOfSyncTimestep(is_last);
1180+
}
1181+
#endif
1182+
}
1183+
11571184
// The maybeUpdateTuning_() lambda callback is defined in SimulatorFullyImplicitBlackoil::runStep()
11581185
// It has to be called for each substep since TUNING might have been changed for next sub step due
11591186
// to ACTIONX (via NEXTSTEP) or WCYCLE keywords.

opm/simulators/wells/BlackoilWellModel.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ template<class Scalar> class WellContributions;
434434

435435
void receiveGroupConstraintsFromMaster();
436436
void sendMasterGroupConstraintsToSlaves();
437+
void rescoupSyncSummaryData();
437438

438439
/// \brief Setup RAII guard for reservoir coupling logger
439440
///

opm/simulators/wells/BlackoilWellModel_impl.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,34 @@ namespace Opm {
642642
constraint_receiver.receiveGroupConstraintsFromMaster();
643643
}
644644

645+
template<typename TypeTag>
646+
void
647+
BlackoilWellModel<TypeTag>::
648+
rescoupSyncSummaryData()
649+
{
650+
// Reservoir coupling: exchange production data between slaves and master.
651+
//
652+
// Master side: after its first substep, the master blocks here until all
653+
// slaves have completed the sync step and sent their production data.
654+
// This ensures evalSummaryState() (called next in endTimeStep) and all
655+
// subsequent master substeps have correct slave production rates.
656+
//
657+
// Slave side: on the last substep of the sync step, the slave sends its
658+
// production data to the master. The master is already waiting at this
659+
// point (blocked on MPI_Recv from its first substep's timeStepSucceeded).
660+
if (this->isReservoirCouplingMaster()) {
661+
if (this->reservoirCouplingMaster().needsSlaveDataReceive()) {
662+
this->receiveSlaveGroupData();
663+
this->reservoirCouplingMaster().setNeedsSlaveDataReceive(false);
664+
}
665+
}
666+
if (this->isReservoirCouplingSlave()) {
667+
if (this->reservoirCouplingSlave().isLastSubstepOfSyncTimestep()) {
668+
this->sendSlaveGroupDataToMaster();
669+
}
670+
}
671+
}
672+
645673
#endif // RESERVOIR_COUPLING_ENABLED
646674

647675
template<typename TypeTag>
@@ -797,6 +825,9 @@ namespace Opm {
797825

798826
this->groupStateHelper().updateNONEProductionGroups();
799827

828+
#ifdef RESERVOIR_COUPLING_ENABLED
829+
this->rescoupSyncSummaryData();
830+
#endif
800831
this->commitWGState();
801832

802833
//reporting output temperatures

0 commit comments

Comments
 (0)