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 <mpi.h>
32
33namespace Dune {
34
35namespace detail {
36
84template<class Struct, auto... Members>
86{
87 static MPI_Datatype getType()
88 {
89 // One-time, thread-safe construction: As a precaution, in case this code will be
90 // run by threads in the future, we want to prevent threads entering the code below
91 // simultaneously to build the datatype. This is done by using std::call_once() with
92 // a static flag.
93 std::call_once(flag_, [] {
94
95 constexpr std::size_t N = sizeof...(Members);
96
97 // Array of block lengths (number of elements per field)
98 // NOTE: The block length is set to 1 for each field (blk.fill(1)),
99 // but is strictly not necessary currently, as the processMember()
100 // function will always set the block length anyway.
101 // However, it is kept here for clarity and future-proofing.
102 std::array<int, N> blk{}; blk.fill(1);
103 // Array of displacements for each field
104 std::array<MPI_Aint, N> disp{};
105 // Array of MPI data types for each field
106 std::array<MPI_Datatype, N> types{};
107
108 // Create a dummy instance of the struct to get the base address
109 Struct dummy{};
110 // Get the base address of the dummy instance
111 MPI_Aint base{};
112 MPI_Get_address(&dummy, &base);
113
114 std::size_t i = 0;
115 // fold expression over the member pointer pack
116 ( processMember<Members>(dummy, base, disp, blk, types, i++), ... );
117
118 MPI_Datatype tmp;
119 // Create the MPI datatype
120 MPI_Type_create_struct(N, blk.data(), disp.data(), types.data(), &tmp);
121 // Resize the datatype to account for possible padding issues
122 MPI_Type_create_resized(tmp, 0, sizeof(Struct), &type_);
123 MPI_Type_commit(&type_);
124 // Free the temporary datatype
125 // Note: This is necessary to avoid memory leaks, as the temporary datatype
126 // created by MPI_Type_create_struct is not automatically freed.
127 MPI_Type_free(&tmp);
128 });
129 return type_;
130 }
131
132 static constexpr bool is_intrinsic = false;
133
134 private:
135 // --- helper to recognise std::array ----------------------------------------
136 template<class T> struct is_std_array : std::false_type {};
137 template<class T, std::size_t N>
138 struct is_std_array<std::array<T, N>> : std::true_type {};
139 // Default case: not an enum
140 template <typename T, typename Enable = void>
141 struct MpiDispatch {
142 using Type = MPITraits<T>;
143 };
144 // Specialization for enums
145 template <typename T>
146 struct MpiDispatch<T, typename std::enable_if<std::is_enum<T>::value>::type> {
147 using Type = MPITraits<typename std::underlying_type<T>::type>;
148 };
149
150 template<auto Member, class Dummy>
151 static void processMember(Dummy& d, MPI_Aint base,
152 std::array<MPI_Aint, sizeof...(Members)>& disp,
153 std::array<int, sizeof...(Members)>& blk,
154 std::array<MPI_Datatype, sizeof...(Members)>& types,
155 std::size_t idx)
156 {
157 using MemberT = std::remove_reference_t<decltype(d.*Member)>;
158
159 MPI_Get_address(&(d.*Member), &disp[idx]);
160 disp[idx] -= base;
161
162 if constexpr (std::is_array_v<MemberT>) {
163 // C array T[N]
164 using Elem = std::remove_extent_t<MemberT>;
165 blk [idx] = std::extent_v<MemberT>;
166 types[idx] = MPITraits<Elem>::getType();
167 }
168 else if constexpr (is_std_array<MemberT>::value) {
169 // std::array<T,N>
170 using Elem = typename MemberT::value_type;
171 blk [idx] = std::tuple_size<MemberT>::value;
172 types[idx] = MPITraits<Elem>::getType();
173 }
174 else {
175 // scalar or enum
176 blk [idx] = 1;
177 using MPIType = typename MpiDispatch<MemberT>::Type;
178 types[idx] = MPIType::getType();
179 }
180 }
181
182 // Initial value of MPI_DATATYPE_NULL is used to indicate that the type
183 // has not been created yet
184 static inline MPI_Datatype type_ = MPI_DATATYPE_NULL;
185 static inline std::once_flag flag_;
186};
187
188// Convenience alias for StructMPITraitsImpl
189template<class Struct, auto... Members>
190using StructMPITraits = StructMPITraitsImpl<Struct, Members...>;
191
192} // namespace Dune::detail
193
207template<class Scalar>
210 ::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>,
211 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::potentials,
212 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::surface_rates,
213 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::voidage_rate,
214 &::Opm::ReservoirCoupling::SlaveGroupProductionData<Scalar>::gas_reinjection_rate
215 > { };
216
230template<class Scalar>
233 ::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>,
234 &::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>::surface_rates,
235 &::Opm::ReservoirCoupling::SlaveGroupInjectionData<Scalar>::reservoir_rates
236 > { };
237} // namespace Dune
238
239#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:86
static constexpr bool is_intrinsic
Definition: ReservoirCouplingMpiTraits.hpp:132
static MPI_Datatype getType()
Definition: ReservoirCouplingMpiTraits.hpp:87
Definition: ReservoirCoupling.hpp:198
Definition: ReservoirCoupling.hpp:184