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
37// -----------------------------------------------------------------------
38// StructMPITraitsImpl -- generic MPI traits implementation for structs
39//
40// Assumes that each field of the struct is either
41// - an already defined MPI type, or
42// - is an array or std::array of an already defined MPI type, or
43// - is an enum with underlying type that is an already defined MPI type
44// ------------------------------------------------------------------------
45template<class Struct, auto... Members>
47{
48 static MPI_Datatype getType()
49 {
50 // One-time, thread-safe construction: As a precaution, in case this code will be
51 // run by threads in the future, we want to prevent threads entering the code below
52 // simultaneously to build the datatype. This is done by using std::call_once() with
53 // a static flag.
54 std::call_once(flag_, [] {
55
56 constexpr std::size_t N = sizeof...(Members);
57
58 // Array of block lengths (number of elements per field)
59 // NOTE: The block length is set to 1 for each field (blk.fill(1)),
60 // but is strictly not necessary currently, as the processMember()
61 // function will always set the block length anyway.
62 // However, it is kept here for clarity and future-proofing.
63 std::array<int, N> blk{}; blk.fill(1);
64 // Array of displacements for each field
65 std::array<MPI_Aint, N> disp{};
66 // Array of MPI data types for each field
67 std::array<MPI_Datatype, N> types{};
68
69 // Create a dummy instance of the struct to get the base address
70 Struct dummy{};
71 // Get the base address of the dummy instance
72 MPI_Aint base{};
73 MPI_Get_address(&dummy, &base);
74
75 std::size_t i = 0;
76 // fold expression over the member pointer pack
77 ( processMember<Members>(dummy, base, disp, blk, types, i++), ... );
78
79 MPI_Datatype tmp;
80 // Create the MPI datatype
81 MPI_Type_create_struct(N, blk.data(), disp.data(), types.data(), &tmp);
82 // Resize the datatype to account for possible padding issues
83 MPI_Type_create_resized(tmp, 0, sizeof(Struct), &type_);
84 MPI_Type_commit(&type_);
85 // Free the temporary datatype
86 // Note: This is necessary to avoid memory leaks, as the temporary datatype
87 // created by MPI_Type_create_struct is not automatically freed.
88 MPI_Type_free(&tmp);
89 });
90 return type_;
91 }
92
93 static constexpr bool is_intrinsic = false;
94
95 private:
96 // --- helper to recognise std::array ----------------------------------------
97 template<class T> struct is_std_array : std::false_type {};
98 template<class T, std::size_t N>
99 struct is_std_array<std::array<T, N>> : std::true_type {};
100 // Default case: not an enum
101 template <typename T, typename Enable = void>
102 struct MpiDispatch {
103 using Type = MPITraits<T>;
104 };
105 // Specialization for enums
106 template <typename T>
107 struct MpiDispatch<T, typename std::enable_if<std::is_enum<T>::value>::type> {
108 using Type = MPITraits<typename std::underlying_type<T>::type>;
109 };
110
111 template<auto Member, class Dummy>
112 static void processMember(Dummy& d, MPI_Aint base,
113 std::array<MPI_Aint, sizeof...(Members)>& disp,
114 std::array<int, sizeof...(Members)>& blk,
115 std::array<MPI_Datatype, sizeof...(Members)>& types,
116 std::size_t idx)
117 {
118 using MemberT = std::remove_reference_t<decltype(d.*Member)>;
119
120 MPI_Get_address(&(d.*Member), &disp[idx]);
121 disp[idx] -= base;
122
123 if constexpr (std::is_array_v<MemberT>) {
124 // C array T[N]
125 using Elem = std::remove_extent_t<MemberT>;
126 blk [idx] = std::extent_v<MemberT>;
127 types[idx] = MPITraits<Elem>::getType();
128 }
129 else if constexpr (is_std_array<MemberT>::value) {
130 // std::array<T,N>
131 using Elem = typename MemberT::value_type;
132 blk [idx] = std::tuple_size<MemberT>::value;
133 types[idx] = MPITraits<Elem>::getType();
134 }
135 else {
136 // scalar or enum
137 blk [idx] = 1;
138 using MPIType = typename MpiDispatch<MemberT>::Type;
139 types[idx] = MPIType::getType();
140 }
141 }
142
143 // Initial value of MPI_DATATYPE_NULL is used to indicate that the type
144 // has not been created yet
145 static inline MPI_Datatype type_ = MPI_DATATYPE_NULL;
146 static inline std::once_flag flag_;
147};
148
149// Convenience alias for StructMPITraitsImpl
150template<class Struct, auto... Members>
151using StructMPITraits = StructMPITraitsImpl<Struct, Members...>;
152
153} // namespace Dune::detail
154
155// Trait for Potentials
156template<>
159 ::Opm::ReservoirCoupling::Potentials,
160 &::Opm::ReservoirCoupling::Potentials::rate> { };
161
162} // namespace Dune
163
164#endif // OPM_RESERVOIR_COUPLING_MPI_TRAITS_HPP
Definition: fvbaseprimaryvariables.hh:141
TYPE getType(const TABLE &table)
Definition: ReservoirCouplingMpiTraits.hpp:47
static constexpr bool is_intrinsic
Definition: ReservoirCouplingMpiTraits.hpp:93
static MPI_Datatype getType()
Definition: ReservoirCouplingMpiTraits.hpp:48
Definition: ReservoirCoupling.hpp:62