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  bool haveDeferredLogger() const { return deferred_logger_ != nullptr; }
52  void info(const std::string &msg) const;
53  void warning(const std::string &msg) const;
54  void setDeferredLogger(DeferredLogger *deferred_logger) { deferred_logger_ = deferred_logger; }
55 
56 private:
57  const Parallel::Communication& comm_;
58  DeferredLogger *deferred_logger_ = nullptr;
59 };
60 
89 public:
90  explicit ScopedLoggerGuard(Logger& logger, DeferredLogger* deferred_logger)
91  : logger_(&logger)
92  {
93  logger_->setDeferredLogger(deferred_logger);
94  }
95 
97  // Only clear if we still own the logger (not moved-from)
98  if (logger_) {
99  logger_->clearDeferredLogger();
100  }
101  }
102 
103  // Prevent copying to ensure single ownership
104  ScopedLoggerGuard(const ScopedLoggerGuard&) = delete;
105  ScopedLoggerGuard& operator=(const ScopedLoggerGuard&) = delete;
106 
107  // Enable moving - required for returning from functions and std::optional
108  ScopedLoggerGuard(ScopedLoggerGuard&& other) noexcept
109  : logger_(other.logger_)
110  {
111  // Transfer ownership: moved-from object becomes inactive and won't clear the logger
112  other.logger_ = nullptr;
113  }
114 
115  ScopedLoggerGuard& operator=(ScopedLoggerGuard&& other) noexcept {
116  if (this != &other) {
117  // Clean up current logger before taking ownership of new one
118  if (logger_) {
119  logger_->clearDeferredLogger();
120  }
121  // Transfer ownership from other
122  logger_ = other.logger_;
123  other.logger_ = nullptr;
124  }
125  return *this;
126  }
127 
128 private:
129  // Use pointer instead of reference to enable move semantics
130  // (references cannot be reassigned, which is required for move operations)
131  Logger* logger_{nullptr};
132 };
133 
134 enum class MessageTag : int {
135  InjectionGroupTargets,
136  MasterGroupNames,
137  MasterGroupNamesSize,
138  MasterStartOfReportStep,
139  NumSlaveGroupConstraints,
141  SlaveActivationDate,
142  SlaveActivationHandshake,
143  SlaveInjectionData,
144  SlaveProcessTermination,
145  SlaveName,
146  SlaveNameSize,
147  SlaveNextReportDate,
148  SlaveNextTimeStep,
149  SlaveProductionData,
150  SlaveSimulationStartDate,
151  SlaveStartOfReportStep,
152 };
153 
156 enum class Phase : std::size_t {
157  Oil = 0, // Matches Opm::Phase::OIL
158  Gas, // Matches Opm::Phase::GAS
159  Water, // Matches Opm::Phase::WATER
160  Count
161 };
162 
168 enum class RateKind {
169  InjectionSurface,
170  InjectionReservoir,
171  ProductionSurface,
172  ProductionNetworkSurface,
173  ProductionReservoir
174 };
175 
176 template <class Scalar>
178  InjectionRates() = default;
179 
180  std::array<Scalar, static_cast<std::size_t>(Phase::Count)> rate{};
181  [[nodiscard]] Scalar& operator[](Phase p) noexcept { return rate[static_cast<std::size_t>(p)]; }
182  [[nodiscard]] Scalar operator[](Phase p) const noexcept { return rate[static_cast<std::size_t>(p)]; }
183 };
184 
185 // Used to communicate potentials for oil, gas, and water rates between slave and master processes
186 template <class Scalar>
187 struct Potentials {
188  std::array<Scalar, static_cast<std::size_t>(Phase::Count)> rate{};
189 
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 template <class Scalar>
196  ProductionRates() = default;
197 
198  explicit ProductionRates(const GuideRate::RateVector& rate_vector)
199  : rate{static_cast<Scalar>(rate_vector.oil_rat),
200  static_cast<Scalar>(rate_vector.gas_rat),
201  static_cast<Scalar>(rate_vector.wat_rat)}
202  {}
203 
204  std::array<Scalar, static_cast<std::size_t>(Phase::Count)> rate{};
205  [[nodiscard]] Scalar& operator[](Phase p) noexcept { return rate[static_cast<std::size_t>(p)]; }
206  [[nodiscard]] Scalar operator[](Phase p) const noexcept { return rate[static_cast<std::size_t>(p)]; }
207 };
208 
209 // Slave group production data sent to the corresponding master group for target calculation.
210 template <class Scalar>
212  // Group production potentials are used by the master group for guiderate calculations
213  Potentials<Scalar> potentials;
214  // Production rates are used by the master group in guiderate calculations
215  // when converting the guide rate target to the phase of the master group.
216  ProductionRates<Scalar> surface_rates; // Surface production rates by phase (network=false)
217  // Network surface rates - computed with network=true, meaning efficiency factors
218  // are 1.0 for groups/wells with GEFAC/WEFAC item 3 = "NO"
219  ProductionRates<Scalar> network_surface_rates; // Surface rates for network calculations
220  // Individual phase reservoir production rates - needed when master's parent group
221  // has RESV control mode, so the conversion uses slave's PVT properties
222  ProductionRates<Scalar> reservoir_rates; // Reservoir production rates by phase
223  Scalar voidage_rate{0.0}; // Reservoir voidage replacement rate
224  Scalar gas_reinjection_rate{0.0}; // Reinjection (surface) rate for the gas phase
225 };
226 
227 // Slave group injection data sent to the corresponding master group for target calculation.
228 template <class Scalar>
230  InjectionRates<Scalar> surface_rates; // Surface injection rates by phase
231  InjectionRates<Scalar> reservoir_rates; // Reservoir injection rates by phase
232 };
233 
234 template <class Scalar>
236  // To save memory and avoid varying size of the struct when serializing
237  // and deserializing the group name, we use an index instead of the full name.
238  std::size_t group_name_idx; // Index of group name in the master group names vector
239  Scalar target; // Target rate for the group
240  Group::InjectionCMode cmode; // Control mode for the group
241  Phase phase; // Phase the target applies to
242 };
243 
244 template <class Scalar>
246  // To save memory and avoid varying size of the struct when serializing
247  // and deserializing the group name, we use an index instead of the full name.
248  std::size_t group_name_idx; // Index of group name in the master group names vector
249  Scalar target; // Target rate for the active control mode
250  Group::ProductionCMode cmode; // Active control mode for the group
251  // Per-rate-type effective limits (-1 = no limit defined in hierarchy).
252  // These are guide-rate-distributed limits from the group hierarchy.
253  Scalar oil_limit;
254  Scalar water_limit;
255  Scalar gas_limit;
256  Scalar liquid_limit;
257  Scalar resv_limit;
258 };
259 
262 template <class Scalar>
264  Scalar oil_limit{-1};
265  Scalar water_limit{-1};
266  Scalar gas_limit{-1};
267  Scalar liquid_limit{-1};
268  Scalar resv_limit{-1};
269 };
270 
271 // Helper functions
272 Phase convertPhaseToReservoirCouplingPhase(::Opm::Phase phase);
273 ::Opm::Phase convertToOpmPhase(const Phase phase);
274 void customErrorHandler_(MPI_Comm* comm, int* err, const std::string &msg);
275 void customErrorHandlerSlave_(MPI_Comm* comm, int* err, ...);
276 void customErrorHandlerMaster_(MPI_Comm* comm, int* err, ...);
277 void setErrhandler(MPI_Comm comm, bool is_master);
278 std::pair<std::vector<char>, std::size_t> serializeStrings(const std::vector<std::string>& data);
279 
283 inline std::string formatDays(double seconds) {
284  double days = seconds / unit::day;
285  return fmt::format(fmt::runtime("{:.0f}s ({:.2f} days)"), seconds, days);
286 }
287 
305 struct Seconds {
307  static constexpr double abstol = 1e-15;
308 
310  static constexpr double reltol = 1e-15;
311 
330  static bool compare_eq(double a, double b);
331 
337  static bool compare_gt(double a, double b);
338 
344  static bool compare_gt_or_eq(double a, double b);
345 
351  static bool compare_lt(double a, double b);
352 
358  static bool compare_lt_or_eq(double a, double b);
359 };
360 
361 } // namespace ReservoirCoupling
362 } // namespace Opm
363 
364 #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:173
static bool compare_gt(double a, double b)
Determines if a is greater than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:194
Definition: ReservoirCoupling.hpp:177
Definition: ReservoirCoupling.hpp:42
Definition: ReservoirCoupling.hpp:229
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
Definition: ReservoirCoupling.hpp:195
Utility class for comparing double values representing epoch dates or elapsed time.
Definition: ReservoirCoupling.hpp:305
Definition: ReservoirCoupling.hpp:187
Definition: DeferredLogger.hpp:56
static constexpr double abstol
Absolute tolerance used for comparisons.
Definition: ReservoirCoupling.hpp:307
static bool compare_lt(double a, double b)
Determines if a is less than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:188
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:200
Definition: ReservoirCoupling.hpp:245
Per-rate-type production limits received from master hierarchy.
Definition: ReservoirCoupling.hpp:263
Definition: ReservoirCoupling.hpp:235
static constexpr double reltol
Relative tolerance used for comparisons.
Definition: ReservoirCoupling.hpp:310
Guard for managing DeferredLogger lifecycle in ReservoirCoupling.
Definition: ReservoirCoupling.hpp:88
Definition: ReservoirCoupling.hpp:211
static bool compare_gt_or_eq(double a, double b)
Determines if a is greater than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:179