libMesh
parallel_algebra.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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 
39 namespace 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 
53 template <typename V>
55 {
56 public:
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 
141 template <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 
158 namespace 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.
166 template <typename T>
167 class StandardType<libMesh::TypeVector<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
168  : public DataType
169 {
170 public:
171  explicit
172  StandardType(const libMesh::TypeVector<T> * example=nullptr)
173  {
174  using libMesh::TypeVector;
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 
235 template <typename T>
236 class StandardType<libMesh::VectorValue<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
237  : public DataType
238 {
239 public:
240  explicit
241  StandardType(const libMesh::VectorValue<T> * example=nullptr)
242  {
243  using libMesh::VectorValue;
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 
305 template <>
306 class StandardType<libMesh::Point> : public DataType
307 {
308 public:
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 
365  StandardType(const StandardType<libMesh::Point> & timpi_mpi_var(t))
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 
376 template <typename T>
377 class OpFunction<libMesh::TypeVector<T>> : public libMesh::TypeVectorOpFunction<libMesh::TypeVector<T>> {};
378 
379 template <typename T>
380 class OpFunction<libMesh::VectorValue<T>> : public libMesh::TypeVectorOpFunction<libMesh::VectorValue<T>> {};
381 
382 template <>
383 class OpFunction<libMesh::Point> : public libMesh::TypeVectorOpFunction<libMesh::Point> {};
384 
385 
386 template <typename T>
387 class Attributes<libMesh::TypeVector<T>> : public libMesh::TypeVectorAttributes<libMesh::TypeVector<T>> {};
388 
389 template <typename T>
390 class Attributes<libMesh::VectorValue<T>> : public libMesh::TypeVectorAttributes<libMesh::VectorValue<T>> {};
391 
392 template <>
393 class 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
400 template <typename T>
401 class StandardType<libMesh::TypeTensor<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
402  : public DataType
403 {
404 public:
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 
414 template <typename T>
415 class StandardType<libMesh::TensorValue<T>, typename std::enable_if<StandardType<T>::is_fixed_type>::type>
416  : public DataType
417 {
418 public:
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 void vector_min(void *invec, void *inoutvec, int *len, MPI_Datatype *)
static void vector_sum(void *invec, void *inoutvec, int *len, MPI_Datatype *)
StandardType(const StandardType< libMesh::Point > &timpi_mpi_var(t))
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.
This class defines a tensor in LIBMESH_DIM dimensional space of type T.
Definition: tensor_tools.h:36
void libmesh_ignore(const Args &...)
bool _is_initialized
Flag that tells if init() has been called.
Definition: libmesh.C:257
This class defines a vector in LIBMESH_DIM dimensional space of type T.
Definition: tensor_tools.h:34
static const bool is_fixed_type
StandardType(const libMesh::Point *example=nullptr)
static void vector_max(void *invec, void *inoutvec, int *len, MPI_Datatype *)
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.