ReservoirCouplingMpiTraits.hpp
Go to the documentation of this file.
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
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
35namespace Dune {
36namespace detail {
37
85template<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
197template<class Struct, auto... Members>
198using 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
209template<class Scalar>
210struct 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 ProductionGroupTarget
220template<class Scalar>
221struct MPITraits<
224 ::Opm::ReservoirCoupling::ProductionGroupTarget<Scalar>,
225 &::Opm::ReservoirCoupling::ProductionGroupTarget<Scalar>::group_name_idx,
226 &::Opm::ReservoirCoupling::ProductionGroupTarget<Scalar>::target,
227 &::Opm::ReservoirCoupling::ProductionGroupTarget<Scalar>::cmode> { };
228
242template<class Scalar>
245 ::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>,
246 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::potentials,
247 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::surface_rates,
248 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::reservoir_rates,
249 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::voidage_rate,
250 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::gas_reinjection_rate
251 > { };
252
266template<class Scalar>
269 ::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>,
270 &::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>::surface_rates,
271 &::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>::reservoir_rates
272 > { };
273} // namespace Dune
274
275#endif // OPM_RESERVOIR_COUPLING_MPI_TRAITS_HPP
Definition: fvbaseprimaryvariables.hh:161
TYPE getType(const TABLE &table)
Generic MPI traits implementation for structs.
Definition: ReservoirCouplingMpiTraits.hpp:87
static constexpr bool is_intrinsic
Definition: ReservoirCouplingMpiTraits.hpp:140
static MPI_Datatype getType()
Definition: ReservoirCouplingMpiTraits.hpp:88
Definition: ReservoirCoupling.hpp:211
Definition: ReservoirCoupling.hpp:221
Definition: ReservoirCoupling.hpp:205
Definition: ReservoirCoupling.hpp:190