opm-simulators
ReservoirCouplingMpiTraits.hpp
1 /*
2  Copyright 2025 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_MPI_TRAITS_HPP
21 #define OPM_RESERVOIR_COUPLING_MPI_TRAITS_HPP
22 
23 #include <dune/common/parallel/mpitraits.hh>
24 
25 #include <opm/simulators/flow/rescoup/ReservoirCoupling.hpp>
26 
27 #include <array>
28 #include <mutex> // For std::call_once
29 #include <tuple> // for std::tuple_size
30 
31 #include <array>
32 #include <mutex> // For std::call_once
33 #include <mpi.h>
34 
35 namespace Dune {
36 namespace detail {
37 
85 template<class Struct, auto... Members>
87 {
88  static MPI_Datatype getType()
89  {
90  // One-time, thread-safe construction: As a precaution, in case this code will be
91  // run by threads in the future, we want to prevent threads entering the code below
92  // simultaneously to build the datatype. This is done by using std::call_once() with
93  // a static flag.
94  std::call_once(flag_, [] {
95 
96  constexpr std::size_t N = sizeof...(Members);
97 
98  // Array of block lengths (number of elements per field)
99  // NOTE: The block length is set to 1 for each field (blk.fill(1)),
100  // but is strictly not necessary currently, as the processMember()
101  // function will always set the block length anyway.
102  // However, it is kept here for clarity and future-proofing.
103  std::array<int, N> blk{}; blk.fill(1);
104  // Array of displacements for each field
105  std::array<MPI_Aint, N> disp{};
106  // Array of MPI data types for each field
107  std::array<MPI_Datatype, N> types{};
108 
109  // Create a dummy instance of the struct to get the base address
110  Struct dummy{};
111  // Get the base address of the dummy instance
112  MPI_Aint base{};
113  MPI_Get_address(&dummy, &base);
114 
115  std::size_t i = 0;
116  // fold expression over the member pointer pack
117  ( processMember<Members>(dummy, base, disp, blk, types, i++), ... );
118 
119  MPI_Datatype tmp;
120  // Create the MPI datatype
121  // The below assertion ensures that the displacements are in declaration order.
122  // It is strictly not necessary, since MPI_Type_create_struct does not require
123  // the displacements to be in declaration order, but it is a good practice
124  // to ensure that the displacements are in declaration order for better readability
125  // and maintainability of the code.
126  for (std::size_t k = 1; k < N; ++k)
127  assert(disp[k-1] < disp[k] && "StructMPITraits member list not in declaration order");
128  MPI_Type_create_struct(N, blk.data(), disp.data(), types.data(), &tmp);
129  // Resize the datatype to account for possible padding issues
130  MPI_Type_create_resized(tmp, 0, sizeof(Struct), &type_);
131  MPI_Type_commit(&type_);
132  // Free the temporary datatype
133  // Note: This is necessary to avoid memory leaks, as the temporary datatype
134  // created by MPI_Type_create_struct is not automatically freed.
135  MPI_Type_free(&tmp);
136  });
137  return type_;
138  }
139 
140  static constexpr bool is_intrinsic = false;
141 
142  private:
143  // --- helper to recognise std::array ----------------------------------------
144  template<class T> struct is_std_array : std::false_type {};
145  template<class T, std::size_t N>
146  struct is_std_array<std::array<T, N>> : std::true_type {};
147  // Default case: not an enum
148  template <typename T, typename Enable = void>
149  struct MpiDispatch {
150  using Type = MPITraits<T>;
151  };
152  // Specialization for enums
153  template <typename T>
154  struct MpiDispatch<T, typename std::enable_if<std::is_enum<T>::value>::type> {
155  using Type = MPITraits<typename std::underlying_type<T>::type>;
156  };
157 
158  template<auto Member, class Dummy>
159  static void processMember(Dummy& d, MPI_Aint base,
160  std::array<MPI_Aint, sizeof...(Members)>& disp,
161  std::array<int, sizeof...(Members)>& blk,
162  std::array<MPI_Datatype, sizeof...(Members)>& types,
163  std::size_t idx)
164  {
165  using MemberT = std::remove_reference_t<decltype(d.*Member)>;
166 
167  MPI_Get_address(&(d.*Member), &disp[idx]);
168  disp[idx] -= base;
169 
170  if constexpr (std::is_array_v<MemberT>) {
171  // C array T[N]
172  using Elem = std::remove_extent_t<MemberT>;
173  blk [idx] = std::extent_v<MemberT>;
174  types[idx] = MPITraits<Elem>::getType();
175  }
176  else if constexpr (is_std_array<MemberT>::value) {
177  // std::array<T,N>
178  using Elem = typename MemberT::value_type;
179  blk [idx] = std::tuple_size<MemberT>::value;
180  types[idx] = MPITraits<Elem>::getType();
181  }
182  else {
183  // scalar or enum
184  blk [idx] = 1;
185  using MPIType = typename MpiDispatch<MemberT>::Type;
186  types[idx] = MPIType::getType();
187  }
188  }
189 
190  // Initial value of MPI_DATATYPE_NULL is used to indicate that the type
191  // has not been created yet
192  static inline MPI_Datatype type_ = MPI_DATATYPE_NULL;
193  static inline std::once_flag flag_;
194 };
195 
196 // Convenience alias for StructMPITraitsImpl
197 template<class Struct, auto... Members>
198 using StructMPITraits = StructMPITraitsImpl<Struct, Members...>;
199 
200 } // namespace Dune::detail
201 
202 // -----------------------------------------------------------------------
203 // MPI traits for ReservoirCoupling structs
204 // The fields of the structs should be listed in the order they are declared
205 // in the struct definition.
206 // -----------------------------------------------------------------------
207 
208 // Trait for InjectionGroupTarget
209 template<class Scalar>
210 struct MPITraits<
213  ::Opm::ReservoirCoupling::InjectionGroupTarget<Scalar>,
214  &::Opm::ReservoirCoupling::InjectionGroupTarget<Scalar>::group_name_idx,
215  &::Opm::ReservoirCoupling::InjectionGroupTarget<Scalar>::target,
216  &::Opm::ReservoirCoupling::InjectionGroupTarget<Scalar>::cmode,
217  &::Opm::ReservoirCoupling::InjectionGroupTarget<Scalar>::phase> { };
218 
219 // Trait for ProductionGroupConstraints
220 template<class Scalar>
221 struct MPITraits<
224  ::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>,
225  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::group_name_idx,
226  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::target,
227  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::cmode,
228  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::oil_limit,
229  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::water_limit,
230  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::gas_limit,
231  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::liquid_limit,
232  &::Opm::ReservoirCoupling::ProductionGroupConstraints<Scalar>::resv_limit> { };
233 
247 template<class Scalar>
250  ::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>,
251  &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::potentials,
252  &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::surface_rates,
253  &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::network_surface_rates,
254  &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::reservoir_rates,
255  &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::voidage_rate,
256  &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::gas_reinjection_rate
257  > { };
258 
272 template<class Scalar>
275  ::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>,
276  &::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>::surface_rates,
277  &::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>::reservoir_rates
278  > { };
279 } // namespace Dune
280 
281 #endif // OPM_RESERVOIR_COUPLING_MPI_TRAITS_HPP
Definition: fvbaseprimaryvariables.hh:161
Definition: ReservoirCoupling.hpp:229
Definition: ReservoirCoupling.hpp:245
Definition: ReservoirCoupling.hpp:235
Definition: ReservoirCoupling.hpp:211
Generic MPI traits implementation for structs.
Definition: ReservoirCouplingMpiTraits.hpp:86