parallel_algebra.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 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 // This class contains all the functionality for bin sorting
23 // Templated on the type of keys you will be sorting and the
24 // type of iterator you will be using.
25 
26 
27 // Local Includes
28 #include "libmesh/libmesh_config.h"
29 #include "libmesh/auto_ptr.h" // deprecated
30 #include "libmesh/parallel.h"
31 #include "libmesh/point.h"
32 #include "libmesh/tensor_value.h"
33 #include "libmesh/vector_value.h"
34 
35 // C++ includes
36 #include <cstddef>
37 #include <memory>
38 
39 namespace libMesh {
40 namespace Parallel {
41 // StandardType<> specializations to return a derived MPI datatype
42 // to handle communication of LIBMESH_DIM-vectors.
43 //
44 // We use MPI_Create_struct here because our vector classes might
45 // have vptrs, and I'd rather not have the datatype break in those
46 // cases.
47 template <typename T>
48 class StandardType<TypeVector<T>> : public DataType
49 {
50 public:
51  explicit
52  StandardType(const TypeVector<T> * example=nullptr) {
53  // We need an example for MPI_Address to use
54  TypeVector<T> * ex;
55  std::unique_ptr<TypeVector<T>> temp;
56  if (example)
57  ex = const_cast<TypeVector<T> *>(example);
58  else
59  {
60  temp.reset(new TypeVector<T>());
61  ex = temp.get();
62  }
63 
64 #ifdef LIBMESH_HAVE_MPI
65  StandardType<T> T_type(&((*ex)(0)));
66 
67  // We require MPI-2 here:
68  int blocklength = LIBMESH_DIM;
69  MPI_Aint displs, start;
70  MPI_Datatype tmptype, type = T_type;
71 
72  libmesh_call_mpi
73  (MPI_Get_address (ex, &start));
74  libmesh_call_mpi
75  (MPI_Get_address (&((*ex)(0)), &displs));
76 
77  // subtract off offset to first value from the beginning of the structure
78  displs -= start;
79 
80  // create a prototype structure
81  libmesh_call_mpi
82  (MPI_Type_create_struct (1, &blocklength, &displs, &type,
83  &tmptype));
84  libmesh_call_mpi
85  (MPI_Type_commit (&tmptype));
86 
87  // resize the structure type to account for padding, if any
88  libmesh_call_mpi
89  (MPI_Type_create_resized (tmptype, 0, sizeof(TypeVector<T>),
90  &_datatype));
91 
92  libmesh_call_mpi
93  (MPI_Type_commit (&_datatype));
94 
95  libmesh_call_mpi
96  (MPI_Type_free (&tmptype));
97 #endif // #ifdef LIBMESH_HAVE_MPI
98  }
99 
100  StandardType(const StandardType<TypeVector<T>> & libmesh_mpi_var(t))
101  : DataType()
102  {
103  libmesh_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
104  }
105 
106  ~StandardType() { this->free(); }
107 };
108 
109 
110 template <typename T>
112 {
113 public:
114  explicit
115  StandardType(const VectorValue<T> * example=nullptr) {
116  // We need an example for MPI_Address to use
117  VectorValue<T> * ex;
118  std::unique_ptr<VectorValue<T>> temp;
119  if (example)
120  ex = const_cast<VectorValue<T> *>(example);
121  else
122  {
123  temp.reset(new VectorValue<T>());
124  ex = temp.get();
125  }
126 
127 #ifdef LIBMESH_HAVE_MPI
128  StandardType<T> T_type(&((*ex)(0)));
129 
130  int blocklength = LIBMESH_DIM;
131  MPI_Aint displs, start;
132  MPI_Datatype tmptype, type = T_type;
133 
134  libmesh_call_mpi
135  (MPI_Get_address (ex, &start));
136  libmesh_call_mpi
137  (MPI_Get_address (&((*ex)(0)), &displs));
138 
139  // subtract off offset to first value from the beginning of the structure
140  displs -= start;
141 
142  // create a prototype structure
143  libmesh_call_mpi
144  (MPI_Type_create_struct (1, &blocklength, &displs, &type,
145  &tmptype));
146  libmesh_call_mpi
147  (MPI_Type_commit (&tmptype));
148 
149  // resize the structure type to account for padding, if any
150  libmesh_call_mpi
151  (MPI_Type_create_resized (tmptype, 0,
152  sizeof(VectorValue<T>),
153  &_datatype));
154 
155  libmesh_call_mpi
156  (MPI_Type_commit (&_datatype));
157 
158  libmesh_call_mpi
159  (MPI_Type_free (&tmptype));
160 #endif // #ifdef LIBMESH_HAVE_MPI
161  }
162 
164  : DataType()
165  {
166 #ifdef LIBMESH_HAVE_MPI
167  libmesh_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
168 #endif
169  }
170 
171  ~StandardType() { this->free(); }
172 };
173 
174 
175 template <>
176 class StandardType<Point> : public DataType
177 {
178 public:
179  explicit
180  StandardType(const Point * example=nullptr)
181  {
182  // Prevent unused variable warnings when !LIBMESH_HAVE_MPI
183  libmesh_ignore(example);
184 
185 #ifdef LIBMESH_HAVE_MPI
186 
187  // We need an example for MPI_Address to use
188  Point * ex;
189 
190  std::unique_ptr<Point> temp;
191  if (example)
192  ex = const_cast<Point *>(example);
193  else
194  {
195  temp.reset(new Point());
196  ex = temp.get();
197  }
198 
199  StandardType<Real> T_type(&((*ex)(0)));
200 
201  int blocklength = LIBMESH_DIM;
202  MPI_Aint displs, start;
203  MPI_Datatype tmptype, type = T_type;
204 
205  libmesh_call_mpi
206  (MPI_Get_address (ex, &start));
207  libmesh_call_mpi
208  (MPI_Get_address (&((*ex)(0)), &displs));
209 
210  // subtract off offset to first value from the beginning of the structure
211  displs -= start;
212 
213  // create a prototype structure
214  libmesh_call_mpi
215  (MPI_Type_create_struct (1, &blocklength, &displs, &type,
216  &tmptype));
217  libmesh_call_mpi
218  (MPI_Type_commit (&tmptype));
219 
220  // resize the structure type to account for padding, if any
221  libmesh_call_mpi
222  (MPI_Type_create_resized (tmptype, 0, sizeof(Point),
223  &_datatype));
224 
225  libmesh_call_mpi
226  (MPI_Type_commit (&_datatype));
227 
228  libmesh_call_mpi
229  (MPI_Type_free (&tmptype));
230 #endif // #ifdef LIBMESH_HAVE_MPI
231  }
232 
233  StandardType(const StandardType<Point> & libmesh_mpi_var(t))
234  : DataType()
235  {
236  libmesh_call_mpi (MPI_Type_dup (t._datatype, &_datatype));
237  }
238 
239  ~StandardType() { this->free(); }
240 };
241 
242 
243 // OpFunction<> specializations to return an MPI_Op version of the
244 // reduction operations on LIBMESH_DIM-vectors.
245 //
246 // We use static variables to minimize the number of MPI datatype
247 // construction calls executed over the course of the program.
248 //
249 // We use a singleton pattern because a global variable would
250 // have tried to call MPI functions before MPI got initialized.
251 //
252 // min() and max() are applied component-wise; this makes them useful
253 // for bounding box reduction operations.
254 template <typename V>
256 {
257 public:
258 #ifdef LIBMESH_HAVE_MPI
259  static void vector_max (void * invec, void * inoutvec, int * len, MPI_Datatype *)
260  {
261  V *in = static_cast<V *>(invec);
262  V *inout = static_cast<V *>(inoutvec);
263  for (int i=0; i != *len; ++i)
264  for (int d=0; d != LIBMESH_DIM; ++d)
265  inout[i](d) = std::max(in[i](d), inout[i](d));
266  }
267 
268  static void vector_min (void * invec, void * inoutvec, int * len, MPI_Datatype *)
269  {
270  V *in = static_cast<V *>(invec);
271  V *inout = static_cast<V *>(inoutvec);
272  for (int i=0; i != *len; ++i)
273  for (int d=0; d != LIBMESH_DIM; ++d)
274  inout[i](d) = std::min(in[i](d), inout[i](d));
275  }
276 
277  static void vector_sum (void * invec, void * inoutvec, int * len, MPI_Datatype *)
278  {
279  V *in = static_cast<V *>(invec);
280  V *inout = static_cast<V *>(inoutvec);
281  for (int i=0; i != *len; ++i)
282  for (int d=0; d != LIBMESH_DIM; ++d)
283  inout[i](d) += in[i](d);
284  }
285 
286  static MPI_Op max()
287  {
288  // _static_op never gets freed, but it only gets committed once
289  // per T, so it's not a *huge* memory leak...
290  static MPI_Op _static_op;
291  static bool _is_initialized = false;
292  if (!_is_initialized)
293  {
294  libmesh_call_mpi
295  (MPI_Op_create (vector_max, /*commute=*/ true,
296  &_static_op));
297 
298  _is_initialized = true;
299  }
300 
301  return _static_op;
302  }
303  static MPI_Op min()
304  {
305  // _static_op never gets freed, but it only gets committed once
306  // per T, so it's not a *huge* memory leak...
307  static MPI_Op _static_op;
308  static bool _is_initialized = false;
309  if (!_is_initialized)
310  {
311  libmesh_call_mpi
312  (MPI_Op_create (vector_min, /*commute=*/ true,
313  &_static_op));
314 
315  _is_initialized = true;
316  }
317 
318  return _static_op;
319  }
320  static MPI_Op sum()
321  {
322  // _static_op never gets freed, but it only gets committed once
323  // per T, so it's not a *huge* memory leak...
324  static MPI_Op _static_op;
325  static bool _is_initialized = false;
326  if (!_is_initialized)
327  {
328  libmesh_call_mpi
329  (MPI_Op_create (vector_sum, /*commute=*/ true,
330  &_static_op));
331 
332  _is_initialized = true;
333  }
334 
335  return _static_op;
336  }
337 
338 #endif // LIBMESH_HAVE_MPI
339 };
340 
341 template <typename T>
342 class OpFunction<TypeVector<T>> : public TypeVectorOpFunction<TypeVector<T>> {};
343 
344 template <typename T>
345 class OpFunction<VectorValue<T>> : public TypeVectorOpFunction<VectorValue<T>> {};
346 
347 template <>
348 class OpFunction<Point> : public TypeVectorOpFunction<Point> {};
349 
350 // StandardType<> specializations to return a derived MPI datatype
351 // to handle communication of LIBMESH_DIM*LIBMESH_DIM-tensors.
352 //
353 // We assume contiguous storage here
354 template <typename T>
355 class StandardType<TypeTensor<T>> : public DataType
356 {
357 public:
358  explicit
359  StandardType(const TypeTensor<T> * example=nullptr) :
360  DataType(StandardType<T>(example ? &((*example)(0,0)) : nullptr), LIBMESH_DIM*LIBMESH_DIM) {}
361 
362  inline ~StandardType() { this->free(); }
363 };
364 
365 template <typename T>
367 {
368 public:
369  explicit
370  StandardType(const TensorValue<T> * example=nullptr) :
371  DataType(StandardType<T>(example ? &((*example)(0,0)) : nullptr), LIBMESH_DIM*LIBMESH_DIM) {}
372 
373  inline ~StandardType() { this->free(); }
374 };
375 } // namespace Parallel
376 } // namespace libMesh
377 
378 #endif // LIBMESH_PARALLEL_ALGEBRA_H
StandardType(const StandardType< VectorValue< T >> &t)
static void vector_max(void *invec, void *inoutvec, int *len, MPI_Datatype *)
StandardType(const TypeTensor< T > *example=nullptr)
StandardType(const StandardType< Point > &libmesh_mpi_var(t))
long double max(long double a, double b)
static void vector_min(void *invec, void *inoutvec, int *len, MPI_Datatype *)
StandardType(const TensorValue< T > *example=nullptr)
void libmesh_ignore(const Args &...)
StandardType(const VectorValue< T > *example=nullptr)
StandardType(const Point *example=nullptr)
StandardType(const TypeVector< T > *example=nullptr)
StandardType(const StandardType< TypeVector< T >> &libmesh_mpi_var(t))
long double min(long double a, double b)
A geometric point in (x,y,z) space.
Definition: point.h:38
static void vector_sum(void *invec, void *inoutvec, int *len, MPI_Datatype *)