libMesh
Loading...
Searching...
No Matches
parallel_algebra.h
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19#ifndef LIBMESH_PARALLEL_ALGEBRA_H
20#define LIBMESH_PARALLEL_ALGEBRA_H
21
22
23// libMesh includes
24#include "libmesh/libmesh_config.h"
25#include "libmesh/point.h"
26#include "libmesh/tensor_value.h"
27#include "libmesh/vector_value.h"
28
29// TIMPI includes
30#include "timpi/attributes.h"
31#include "timpi/op_function.h"
32#include "timpi/standard_type.h"
33
34// C++ includes
35#include <cstddef>
36#include <memory>
37#include <type_traits>
38
39namespace libMesh {
40
41// OpFunction<> specializations to return an MPI_Op version of the
42// reduction operations on LIBMESH_DIM-vectors.
43//
44// We use static variables to minimize the number of MPI datatype
45// construction calls executed over the course of the program.
46//
47// We use a singleton pattern because a global variable would
48// have tried to call MPI functions before MPI got initialized.
49//
50// min() and max() are applied component-wise; this makes them useful
51// for bounding box reduction operations.
52
53template <typename V>
55{
56public:
57#ifdef LIBMESH_HAVE_MPI
58 static void vector_max (void * invec, void * inoutvec, int * len, MPI_Datatype *)
59 {
60 V *in = static_cast<V *>(invec);
61 V *inout = static_cast<V *>(inoutvec);
62 for (int i=0; i != *len; ++i)
63 for (int d=0; d != LIBMESH_DIM; ++d)
64 inout[i](d) = std::max(in[i](d), inout[i](d));
65 }
66
67 static void vector_min (void * invec, void * inoutvec, int * len, MPI_Datatype *)
68 {
69 V *in = static_cast<V *>(invec);
70 V *inout = static_cast<V *>(inoutvec);
71 for (int i=0; i != *len; ++i)
72 for (int d=0; d != LIBMESH_DIM; ++d)
73 inout[i](d) = std::min(in[i](d), inout[i](d));
74 }
75
76 static void vector_sum (void * invec, void * inoutvec, int * len, MPI_Datatype *)
77 {
78 V *in = static_cast<V *>(invec);
79 V *inout = static_cast<V *>(inoutvec);
80 for (int i=0; i != *len; ++i)
81 for (int d=0; d != LIBMESH_DIM; ++d)
82 inout[i](d) += in[i](d);
83 }
84
85 static MPI_Op max()
86 {
87 // _static_op never gets freed, but it only gets committed once
88 // per T, so it's not a *huge* memory leak...
89 static MPI_Op _static_op;
90 static bool _is_initialized = false;
91 if (!_is_initialized)
92 {
93 timpi_call_mpi
94 (MPI_Op_create (vector_max, /*commute=*/ true,
95 &_static_op));
96
97 _is_initialized = true;
98 }
99
100 return _static_op;
101 }
102 static MPI_Op min()
103 {
104 // _static_op never gets freed, but it only gets committed once
105 // per T, so it's not a *huge* memory leak...
106 static MPI_Op _static_op;
107 static bool _is_initialized = false;
108 if (!_is_initialized)
109 {
110 timpi_call_mpi
111 (MPI_Op_create (vector_min, /*commute=*/ true,
112 &_static_op));
113
114 _is_initialized = true;
115 }
116
117 return _static_op;
118 }
119 static MPI_Op sum()
120 {
121 // _static_op never gets freed, but it only gets committed once
122 // per T, so it's not a *huge* memory leak...
123 static MPI_Op _static_op;
124 static bool _is_initialized = false;
125 if (!_is_initialized)
126 {
127 timpi_call_mpi
128 (MPI_Op_create (vector_sum, /*commute=*/ true,
129 &_static_op));
130
131 _is_initialized = true;
132 }
133
134 return _static_op;
135 }
136
137#endif // LIBMESH_HAVE_MPI
138};
139
140
141template <typename V>
143{
144 static const bool has_min_max = true;
145 static void set_lowest(V & x) {
146 for (int d=0; d != LIBMESH_DIM; ++d)
147 TIMPI::Attributes<typename std::remove_reference<decltype(x(d))>::type>::set_lowest(x(d));
148 }
149 static void set_highest(V & x) {
150 for (int d=0; d != LIBMESH_DIM; ++d)
151 TIMPI::Attributes<typename std::remove_reference<decltype(x(d))>::type>::set_highest(x(d));
152 }
153};
154
155} // namespace libMesh
156
157
158namespace TIMPI {
159
160// StandardType<> specializations to return a derived MPI datatype
161// to handle communication of LIBMESH_DIM-vectors.
162//
163// We use MPI_Create_struct here because our vector classes might
164// have vptrs, and I'd rather not have the datatype break in those
165// cases.
166template <typename T>
167class StandardType<libMesh::TypeVector<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
168 : public DataType
169{
170public:
171 explicit
172 StandardType(const libMesh::TypeVector<T> * example=nullptr)
173 {
175
176 // We need an example for MPI_Address to use
177 TypeVector<T> * ex;
178 std::unique_ptr<TypeVector<T>> temp;
179 if (example)
180 ex = const_cast<TypeVector<T> *>(example);
181 else
182 {
183 temp = std::make_unique<TypeVector<T>>();
184 ex = temp.get();
185 }
186
187#ifdef LIBMESH_HAVE_MPI
188 StandardType<T> T_type(&((*ex)(0)));
189
190 // We require MPI-2 here:
191 int blocklength = LIBMESH_DIM;
192 MPI_Aint displs, start;
193 MPI_Datatype tmptype, type = T_type;
194
195 timpi_call_mpi
196 (MPI_Get_address (ex, &start));
197 timpi_call_mpi
198 (MPI_Get_address (&((*ex)(0)), &displs));
199
200 // subtract off offset to first value from the beginning of the structure
201 displs -= start;
202
203 // create a prototype structure
204 timpi_call_mpi
205 (MPI_Type_create_struct (1, &blocklength, &displs, &type,
206 &tmptype));
207 timpi_call_mpi
208 (MPI_Type_commit (&tmptype));
209
210 // resize the structure type to account for padding, if any
211 timpi_call_mpi
212 (MPI_Type_create_resized (tmptype, 0, sizeof(TypeVector<T>),
213 &_datatype));
214
215 timpi_call_mpi
216 (MPI_Type_commit (&_datatype));
217
218 timpi_call_mpi
219 (MPI_Type_free (&tmptype));
220#endif // #ifdef LIBMESH_HAVE_MPI
221 }
222
224 : DataType()
225 {
226 timpi_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
227 }
228
229 ~StandardType() { this->free(); }
230
231 static const bool is_fixed_type = true;
232};
233
234
235template <typename T>
236class StandardType<libMesh::VectorValue<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
237 : public DataType
238{
239public:
240 explicit
241 StandardType(const libMesh::VectorValue<T> * example=nullptr)
242 {
244
245 // We need an example for MPI_Address to use
246 VectorValue<T> * ex;
247 std::unique_ptr<VectorValue<T>> temp;
248 if (example)
249 ex = const_cast<VectorValue<T> *>(example);
250 else
251 {
252 temp = std::make_unique<VectorValue<T>>();
253 ex = temp.get();
254 }
255
256#ifdef LIBMESH_HAVE_MPI
257 StandardType<T> T_type(&((*ex)(0)));
258
259 int blocklength = LIBMESH_DIM;
260 MPI_Aint displs, start;
261 MPI_Datatype tmptype, type = T_type;
262
263 timpi_call_mpi
264 (MPI_Get_address (ex, &start));
265 timpi_call_mpi
266 (MPI_Get_address (&((*ex)(0)), &displs));
267
268 // subtract off offset to first value from the beginning of the structure
269 displs -= start;
270
271 // create a prototype structure
272 timpi_call_mpi
273 (MPI_Type_create_struct (1, &blocklength, &displs, &type,
274 &tmptype));
275 timpi_call_mpi
276 (MPI_Type_commit (&tmptype));
277
278 // resize the structure type to account for padding, if any
279 timpi_call_mpi
280 (MPI_Type_create_resized (tmptype, 0,
281 sizeof(VectorValue<T>),
282 &_datatype));
283
284 timpi_call_mpi
285 (MPI_Type_commit (&_datatype));
286
287 timpi_call_mpi
288 (MPI_Type_free (&tmptype));
289#endif // #ifdef LIBMESH_HAVE_MPI
290 }
291
293 : DataType()
294 {
295#ifdef LIBMESH_HAVE_MPI
296 timpi_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
297#endif
298 }
299
300 ~StandardType() { this->free(); }
301
302 static const bool is_fixed_type = true;
303};
304
305template <>
306class StandardType<libMesh::Point> : public DataType
307{
308public:
309 explicit
310 StandardType(const libMesh::Point * example=nullptr)
311 {
312 using libMesh::Point;
313
314 // Prevent unused variable warnings when !LIBMESH_HAVE_MPI
315 libmesh_ignore(example);
316
317#ifdef LIBMESH_HAVE_MPI
318
319 // We need an example for MPI_Address to use
320 Point * ex;
321
322 std::unique_ptr<Point> temp;
323 if (example)
324 ex = const_cast<Point *>(example);
325 else
326 {
327 temp = std::make_unique<Point>();
328 ex = temp.get();
329 }
330
331 StandardType<libMesh::Real> T_type(&((*ex)(0)));
332
333 int blocklength = LIBMESH_DIM;
334 MPI_Aint displs, start;
335 MPI_Datatype tmptype, type = T_type;
336
337 timpi_call_mpi
338 (MPI_Get_address (ex, &start));
339 timpi_call_mpi
340 (MPI_Get_address (&((*ex)(0)), &displs));
341
342 // subtract off offset to first value from the beginning of the structure
343 displs -= start;
344
345 // create a prototype structure
346 timpi_call_mpi
347 (MPI_Type_create_struct (1, &blocklength, &displs, &type,
348 &tmptype));
349 timpi_call_mpi
350 (MPI_Type_commit (&tmptype));
351
352 // resize the structure type to account for padding, if any
353 timpi_call_mpi
354 (MPI_Type_create_resized (tmptype, 0, sizeof(Point),
355 &_datatype));
356
357 timpi_call_mpi
358 (MPI_Type_commit (&_datatype));
359
360 timpi_call_mpi
361 (MPI_Type_free (&tmptype));
362#endif // #ifdef LIBMESH_HAVE_MPI
363 }
364
366 : DataType()
367 {
368 timpi_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
369 }
370
371 ~StandardType() { this->free(); }
372
373 static const bool is_fixed_type = true;
374};
375
376template <typename T>
377class OpFunction<libMesh::TypeVector<T>> : public libMesh::TypeVectorOpFunction<libMesh::TypeVector<T>> {};
378
379template <typename T>
380class OpFunction<libMesh::VectorValue<T>> : public libMesh::TypeVectorOpFunction<libMesh::VectorValue<T>> {};
381
382template <>
383class OpFunction<libMesh::Point> : public libMesh::TypeVectorOpFunction<libMesh::Point> {};
384
385
386template <typename T>
387class Attributes<libMesh::TypeVector<T>> : public libMesh::TypeVectorAttributes<libMesh::TypeVector<T>> {};
388
389template <typename T>
390class Attributes<libMesh::VectorValue<T>> : public libMesh::TypeVectorAttributes<libMesh::VectorValue<T>> {};
391
392template <>
393class Attributes<libMesh::Point> : public libMesh::TypeVectorAttributes<libMesh::Point> {};
394
395
396// StandardType<> specializations to return a derived MPI datatype
397// to handle communication of LIBMESH_DIM*LIBMESH_DIM-tensors.
398//
399// We assume contiguous storage here
400template <typename T>
401class StandardType<libMesh::TypeTensor<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
402 : public DataType
403{
404public:
405 explicit
406 StandardType(const libMesh::TypeTensor<T> * example=nullptr) :
407 DataType(StandardType<T>(example ? &((*example)(0,0)) : nullptr), LIBMESH_DIM*LIBMESH_DIM) {}
408
409 inline ~StandardType() { this->free(); }
410
411 static const bool is_fixed_type = true;
412};
413
414template <typename T>
415class StandardType<libMesh::TensorValue<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
416 : public DataType
417{
418public:
419 explicit
420 StandardType(const libMesh::TensorValue<T> * example=nullptr) :
421 DataType(StandardType<T>(example ? &((*example)(0,0)) : nullptr), LIBMESH_DIM*LIBMESH_DIM) {}
422
423 inline ~StandardType() { this->free(); }
424
425 static const bool is_fixed_type = true;
426};
427} // namespace TIMPI
428
429#endif // LIBMESH_PARALLEL_ALGEBRA_H
static const bool is_fixed_type
StandardType(const libMesh::Point *example=nullptr)
StandardType(const StandardType< libMesh::Point > &timpi_mpi_var(t))
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
This class defines a tensor in LIBMESH_DIM dimensional space of type T.
Definition type_tensor.h:53
static void vector_min(void *invec, void *inoutvec, int *len, MPI_Datatype *)
static void vector_sum(void *invec, void *inoutvec, int *len, MPI_Datatype *)
static void vector_max(void *invec, void *inoutvec, int *len, MPI_Datatype *)
This class defines a vector in LIBMESH_DIM dimensional space of type T.
Definition type_vector.h:63
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The libMesh namespace provides an interface to certain functionality in the library.