opm-simulators
ReservoirCoupling.hpp
1 /*
2  Copyright 2024 Equinor ASA
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_RESERVOIR_COUPLING_HPP
21 #define OPM_RESERVOIR_COUPLING_HPP
22 #include <opm/simulators/utils/DeferredLogger.hpp>
23 #include <opm/simulators/utils/ParallelCommunication.hpp>
24 #include <opm/input/eclipse/Schedule/Group/Group.hpp>
25 #include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
26 #include <opm/input/eclipse/Units/Units.hpp>
27 
28 #include <dune/common/parallel/mpitraits.hh>
29 
30 #include <fmt/format.h>
31 
32 #include <mpi.h>
33 #include <cmath>
34 #include <iostream>
35 #include <memory>
36 #include <stdexcept>
37 #include <vector>
38 
39 namespace Opm {
40 namespace ReservoirCoupling {
41 
42 class Logger {
43 public:
44  Logger() = delete; // No default constructor - must have comm
45  explicit Logger(const Parallel::Communication& comm) : comm_(comm) {}
46 
47  void clearDeferredLogger() { deferred_logger_ = nullptr; }
48  void debug(const std::string &msg) const;
49  DeferredLogger& deferredLogger() { return *deferred_logger_; }
50  DeferredLogger& deferredLogger() const { return *deferred_logger_; }
51  void error(const std::string &msg) const;
52  bool haveDeferredLogger() const { return deferred_logger_ != nullptr; }
53  void info(const std::string &msg) const;
54  void warning(const std::string &msg) const;
55  void setDeferredLogger(DeferredLogger *deferred_logger) { deferred_logger_ = deferred_logger; }
56 
57 private:
58  template<typename DeferredFn, typename OpmLogFn>
59  void forward_(const std::string &msg, DeferredFn deferred_fn, OpmLogFn opmlog_fn) const;
60 
61  const Parallel::Communication& comm_;
62  DeferredLogger *deferred_logger_ = nullptr;
63 };
64 
93 public:
94  explicit ScopedLoggerGuard(Logger& logger, DeferredLogger* deferred_logger)
95  : logger_(&logger)
96  {
97  logger_->setDeferredLogger(deferred_logger);
98  }
99 
100  ~ScopedLoggerGuard() {
101  // Only clear if we still own the logger (not moved-from)
102  if (logger_) {
103  logger_->clearDeferredLogger();
104  }
105  }
106 
107  // Prevent copying to ensure single ownership
108  ScopedLoggerGuard(const ScopedLoggerGuard&) = delete;
109  ScopedLoggerGuard& operator=(const ScopedLoggerGuard&) = delete;
110 
111  // Enable moving - required for returning from functions and std::optional
112  ScopedLoggerGuard(ScopedLoggerGuard&& other) noexcept
113  : logger_(other.logger_)
114  {
115  // Transfer ownership: moved-from object becomes inactive and won't clear the logger
116  other.logger_ = nullptr;
117  }
118 
119  ScopedLoggerGuard& operator=(ScopedLoggerGuard&& other) noexcept {
120  if (this != &other) {
121  // Clean up current logger before taking ownership of new one
122  if (logger_) {
123  logger_->clearDeferredLogger();
124  }
125  // Transfer ownership from other
126  logger_ = other.logger_;
127  other.logger_ = nullptr;
128  }
129  return *this;
130  }
131 
132 private:
133  // Use pointer instead of reference to enable move semantics
134  // (references cannot be reassigned, which is required for move operations)
135  Logger* logger_{nullptr};
136 };
137 
138 enum class MessageTag : int {
139  CoupledNetworkActiveStatus,
140  InjectionGroupTargets,
141  MasterGroupNames,
142  MasterGroupNamesSize,
143  MasterGroupNodePressures,
144  MasterInitStatus,
145  MasterStartOfReportStep,
146  NumMasterGroupNodePressures,
147  NumSlaveGroupConstraints,
149  SlaveActivationDate,
150  SlaveActivationHandshake,
151  SlaveInjectionData,
152  SlaveProcessTermination,
153  SlaveName,
154  SlaveNameSize,
155  SlaveNextReportDate,
156  SlaveNextTimeStep,
157  SlaveProductionData,
158  SlaveSimulationStartDate,
159  SlaveStartOfReportStep,
160  SlaveStatus,
161 };
162 
165 enum class Phase : std::size_t {
166  Oil = 0, // Matches Opm::Phase::OIL
167  Gas, // Matches Opm::Phase::GAS
168  Water, // Matches Opm::Phase::WATER
169  Count
170 };
171 
177 enum class RateKind {
178  InjectionSurface,
179  InjectionReservoir,
180  ProductionSurface,
181  ProductionNetworkSurface,
182  ProductionReservoir
183 };
184 
185 template <class Scalar>
187  InjectionRates() = default;
188 
189  std::array<Scalar, static_cast<std::size_t>(Phase::Count)> rate{};
190  [[nodiscard]] Scalar& operator[](Phase p) noexcept { return rate[static_cast<std::size_t>(p)]; }
191  [[nodiscard]] Scalar operator[](Phase p) const noexcept { return rate[static_cast<std::size_t>(p)]; }
192 };
193 
194 // Used to communicate potentials for oil, gas, and water rates between slave and master processes
195 template <class Scalar>
196 struct Potentials {
197  std::array<Scalar, static_cast<std::size_t>(Phase::Count)> rate{};
198 
199  [[nodiscard]] Scalar& operator[](Phase p) noexcept { return rate[static_cast<std::size_t>(p)]; }
200  [[nodiscard]] Scalar operator[](Phase p) const noexcept { return rate[static_cast<std::size_t>(p)]; }
201 };
202 
203 template <class Scalar>
205  ProductionRates() = default;
206 
207  explicit ProductionRates(const GuideRate::RateVector& rate_vector)
208  : rate{static_cast<Scalar>(rate_vector.oil_rat),
209  static_cast<Scalar>(rate_vector.gas_rat),
210  static_cast<Scalar>(rate_vector.wat_rat)}
211  {}
212 
213  std::array<Scalar, static_cast<std::size_t>(Phase::Count)> rate{};
214  [[nodiscard]] Scalar& operator[](Phase p) noexcept { return rate[static_cast<std::size_t>(p)]; }
215  [[nodiscard]] Scalar operator[](Phase p) const noexcept { return rate[static_cast<std::size_t>(p)]; }
216 };
217 
218 // Slave group production data sent to the corresponding master group for target calculation.
219 template <class Scalar>
221  // Group production potentials are used by the master group for guiderate calculations
222  Potentials<Scalar> potentials;
223  // Production rates are used by the master group in guiderate calculations
224  // when converting the guide rate target to the phase of the master group.
225  ProductionRates<Scalar> surface_rates; // Surface production rates by phase (network=false)
226  // Network surface rates - computed with network=true, meaning efficiency factors
227  // are 1.0 for groups/wells with GEFAC/WEFAC item 3 = "NO"
228  ProductionRates<Scalar> network_surface_rates; // Surface rates for network calculations
229  // Individual phase reservoir production rates - needed when master's parent group
230  // has RESV control mode, so the conversion uses slave's PVT properties
231  ProductionRates<Scalar> reservoir_rates; // Reservoir production rates by phase
232  Scalar voidage_rate{0.0}; // Reservoir voidage replacement rate
233  Scalar gas_reinjection_rate{0.0}; // Reinjection (surface) rate for the gas phase
234 };
235 
236 // Slave group injection data sent to the corresponding master group for target calculation.
237 template <class Scalar>
239  InjectionRates<Scalar> surface_rates; // Surface injection rates by phase
240  InjectionRates<Scalar> reservoir_rates; // Reservoir injection rates by phase
241 };
242 
243 template <class Scalar>
245  // To save memory and avoid varying size of the struct when serializing
246  // and deserializing the group name, we use an index instead of the full name.
247  std::size_t group_name_idx; // Index of group name in the master group names vector
248  Scalar target; // Target rate for the group
249  Group::InjectionCMode cmode; // Control mode for the group
250  Phase phase; // Phase the target applies to
251 };
252 
253 template <class Scalar>
255  // To save memory and avoid varying size of the struct when serializing
256  // and deserializing the group name, we use an index instead of the full name.
257  std::size_t group_name_idx; // Index of group name in the master group names vector
258  Scalar target; // Target rate for the active control mode
259  Group::ProductionCMode cmode; // Active control mode for the group
260  // Per-rate-type effective limits (-1 = no limit defined in hierarchy).
261  // These are guide-rate-distributed limits from the group hierarchy.
262  Scalar oil_limit;
263  Scalar water_limit;
264  Scalar gas_limit;
265  Scalar liquid_limit;
266  Scalar resv_limit;
267 };
268 
271 template <class Scalar>
273  Scalar oil_limit{-1};
274  Scalar water_limit{-1};
275  Scalar gas_limit{-1};
276  Scalar liquid_limit{-1};
277  Scalar resv_limit{-1};
278 };
279 
286 template <class Scalar>
288  // To save memory and avoid varying size of the struct when serializing
289  // and deserializing the group name, we use an index instead of the full name.
290  std::size_t group_name_idx; // Index of group name in the master group names vector
291  Scalar pressure; // Network-leaf node pressure (SI units, Pa)
292 };
293 
294 // Helper functions
295 Phase convertPhaseToReservoirCouplingPhase(::Opm::Phase phase);
296 ::Opm::Phase convertToOpmPhase(const Phase phase);
297 void customErrorHandler_(MPI_Comm* comm, int* err, const std::string &msg);
298 void customErrorHandlerSlave_(MPI_Comm* comm, int* err, ...);
299 void customErrorHandlerMaster_(MPI_Comm* comm, int* err, ...);
300 void setErrhandler(MPI_Comm comm, bool is_master);
301 std::pair<std::vector<char>, std::size_t> serializeStrings(const std::vector<std::string>& data);
302 
306 inline std::string formatDays(double seconds) {
307  double days = seconds / unit::day;
308  return fmt::format(fmt::runtime("{:.0f}s ({:.2f} days)"), seconds, days);
309 }
310 
328 struct Seconds {
330  static constexpr double abstol = 1e-15;
331 
333  static constexpr double reltol = 1e-15;
334 
353  static bool compare_eq(double a, double b);
354 
360  static bool compare_gt(double a, double b);
361 
367  static bool compare_gt_or_eq(double a, double b);
368 
374  static bool compare_lt(double a, double b);
375 
381  static bool compare_lt_or_eq(double a, double b);
382 };
383 
384 } // namespace ReservoirCoupling
385 } // namespace Opm
386 
387 #endif // OPM_RESERVOIR_COUPLING_HPP
static bool compare_eq(double a, double b)
Determines if two double values are equal within a specified tolerance.
Definition: ReservoirCoupling.cpp:177
static bool compare_gt(double a, double b)
Determines if a is greater than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:198
Master-computed network-leaf node pressure for a single master group.
Definition: ReservoirCoupling.hpp:287
Definition: ReservoirCoupling.hpp:186
Definition: ReservoirCoupling.hpp:42
Definition: ReservoirCoupling.hpp:238
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Definition: ReservoirCoupling.hpp:204
Utility class for comparing double values representing epoch dates or elapsed time.
Definition: ReservoirCoupling.hpp:328
Definition: ReservoirCoupling.hpp:196
Definition: DeferredLogger.hpp:56
static constexpr double abstol
Absolute tolerance used for comparisons.
Definition: ReservoirCoupling.hpp:330
static bool compare_lt(double a, double b)
Determines if a is less than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:192
static bool compare_lt_or_eq(double a, double b)
Determines if a is less than or equal to b within the specified tolerance.
Definition: ReservoirCoupling.cpp:204
Definition: ReservoirCoupling.hpp:254
Per-rate-type production limits received from master hierarchy.
Definition: ReservoirCoupling.hpp:272
Definition: ReservoirCoupling.hpp:244
static constexpr double reltol
Relative tolerance used for comparisons.
Definition: ReservoirCoupling.hpp:333
Guard for managing DeferredLogger lifecycle in ReservoirCoupling.
Definition: ReservoirCoupling.hpp:92
Definition: ReservoirCoupling.hpp:220
static bool compare_gt_or_eq(double a, double b)
Determines if a is greater than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:183