linear_algebra_distribution.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
27
28namespace oomph
29{
30 //============================================================================
31 /// Sets the distribution. Takes first_row, local_nrow and
32 /// global_nrow as arguments. If global_nrow is not provided or equal to
33 /// 0 then it is computed automatically
34 //============================================================================
36 const unsigned& first_row,
37 const unsigned& local_nrow,
38 const unsigned& global_nrow)
39 {
40 // copy the communicator
41 delete Comm_pt;
42 Comm_pt = new OomphCommunicator(*comm_pt);
43
44 // get the rank and the number of processors
45 int my_rank = Comm_pt->my_rank();
46 int nproc = Comm_pt->nproc();
47
48 // resize the storage
49 First_row.clear();
50 First_row.resize(nproc);
51 Nrow_local.clear();
52 Nrow_local.resize(nproc);
53
54 // set first row and local nrow for this processor
57
58#ifdef OOMPH_HAS_MPI
59 // gather the First_row vector
62 1,
64 &Nrow_local[0],
65 1,
67 Comm_pt->mpi_comm());
68
69 // gather the Nrow_local vector
70 unsigned my_first_row = First_row[my_rank];
72 1,
74 &First_row[0],
75 1,
77 Comm_pt->mpi_comm());
78#endif
79
80 // if global nrow is not provided then compute by summing local_nrow over
81 // all processors
82 if (global_nrow == 0)
83 {
84 if (nproc == 1)
85 {
87 }
88 else
89 {
90 Nrow = 0;
91 for (int p = 0; p < nproc; p++)
92 {
93 Nrow += Nrow_local[p];
94 }
95 }
96 }
97 else
98 {
100 }
101
102 // the distribution is true, unless we only have 1 processor
103 if (nproc != 1)
104 {
105 Distributed = true;
106 }
107 else
108 {
109 Distributed = false;
110 }
111
112#ifdef OOMPH_HAS_MPI
113#ifdef PARANOID
114 // paranoid check that the distribution works
115
116
117 // check that none of the processors partition overlap
118 for (int p = 0; p < nproc; p++)
119 {
120 if (Nrow_local[p] > 0)
121 {
122 for (int pp = p + 1; pp < nproc; pp++)
123 {
124 if (Nrow_local[pp] > 0)
125 {
126 if ((First_row[p] >= First_row[pp] &&
128 (First_row[p] + Nrow_local[p] - 1 >= First_row[pp] &&
129 First_row[p] + Nrow_local[p] - 1 <
131 {
132 std::ostringstream error_message;
133 error_message
134 << "The distributed rows on processor " << p
135 << " and processor " << pp << " overlap.\n"
136 << "Processor " << p << " : first_row = " << First_row[p]
137 << ", nrow = " << Nrow_local[p] << ".\n"
138 << "Processor " << pp << " : first_row = " << First_row[pp]
139 << ", nrow = " << Nrow_local[pp] << ".\n";
140 throw OomphLibWarning(
141 error_message.str(),
142 "LinearAlgebraDistribution::distribute(...)",
144 }
145 }
146 }
147 }
148 }
149
150 // check that no processor has a row with a global row index greater than
151 // the number of global rows
152 for (int p = 0; p < nproc; p++)
153 {
154 if (First_row[p] + Nrow_local[p] > Nrow)
155 {
156 std::ostringstream error_message;
157 error_message << "Processor " << p << " contains rows " << First_row[p]
158 << " to " << First_row[p] + Nrow_local[p] - 1
159 << " but there are only " << Nrow << " to be distributed."
160 << std::endl;
161 throw OomphLibWarning(error_message.str(),
162 "LinearAlgebraDistribution::distribute(...)",
164 }
165 }
166#endif
167#endif
168 }
169
170 //============================================================================
171 /// Uniformly distribute global_nrow over all processors where
172 /// processors 0 holds approximately the first
173 /// global_nrow/n_proc, processor 1 holds the next
174 /// global_nrow/n_proc and so on...
175 //============================================================================
177 const unsigned& global_nrow,
178 const bool& distribute)
179 {
180 // copy the communicator
181 delete Comm_pt;
182 Comm_pt = new OomphCommunicator(*comm_pt);
183
184 // delete existing storage
185 First_row.clear();
186 Nrow_local.clear();
187
188 // set global nrow
190
191 // store the distributed flag
192 Distributed = distribute;
193
194#ifdef OOMPH_HAS_MPI
195
196 // if distributed object then compute uniform distribution
197 if (distribute == true)
198 {
199 // get the number of processors
200 int nproc = Comm_pt->nproc();
201
202 // resize the storage
203 First_row.resize(nproc);
204 Nrow_local.resize(nproc);
205
206 // compute first row
207 for (int p = 0; p < nproc; p++)
208 {
209 First_row[p] =
210 unsigned((double(p) / double(nproc)) * double(global_nrow));
211 }
212
213 // compute local nrow
214 for (int p = 0; p < nproc - 1; p++)
215 {
216 Nrow_local[p] = First_row[p + 1] - First_row[p];
217 }
219 }
220#endif
221 }
222
223 //============================================================================
224 /// helper method for the =assignment operator and copy constructor
225 //============================================================================
228 {
229 // delete the existing storage
230 First_row.clear();
231 Nrow_local.clear();
232
233 // if new_dist is not setup
234 if (new_dist.communicator_pt() == 0)
235 {
236 delete Comm_pt;
237 Comm_pt = 0;
238 Distributed = true;
239 Nrow = 0;
240 }
241 else
242 {
243 // copy the communicator
244 delete Comm_pt;
245 Comm_pt = new OomphCommunicator(*new_dist.communicator_pt());
246
247 // the new distribution is distributed
248 if (new_dist.distributed())
249 {
250 First_row = new_dist.first_row_vector();
251 Nrow_local = new_dist.nrow_local_vector();
252
253 Distributed = true;
254 }
255 // else if the new ditribution is not distributed
256 else
257 {
258 Distributed = false;
259 }
260 Nrow = new_dist.nrow();
261 }
262 }
263
264
265 //============================================================================
266 /// operator==
267 //============================================================================
270 {
271#ifdef OOMPH_HAS_MPI
272 // compare the communicators
273 if (!((*Comm_pt) == (*other_dist.communicator_pt())))
274 {
275 return false;
276 }
277
278 // compare Distributed
279 if (Distributed != other_dist.distributed())
280 {
281 return false;
282 }
283
284 // if not distributed compare nrow
285 if (!Distributed)
286 {
287 if (other_dist.nrow() == Nrow)
288 {
289 return true;
290 }
291 return false;
292 }
293
294 // compare
295 bool flag = true;
296 int nproc = Comm_pt->nproc();
297 for (int i = 0; i < nproc && flag == true; i++)
298 {
299 if (other_dist.first_row(i) != First_row[i] ||
300 other_dist.nrow_local(i) != Nrow_local[i])
301 {
302 flag = false;
303 }
304 }
305 return flag;
306#else
307 if (other_dist.nrow() == Nrow)
308 {
309 return true;
310 }
311 return false;
312#endif
313 }
314
315 //=============================================================================
316 /// output operator
317 //=============================================================================
318 std::ostream& operator<<(std::ostream& stream,
320 {
321 stream << "nrow()=" << dist.nrow() << ", first_row()=" << dist.first_row()
322 << ", nrow_local()=" << dist.nrow_local()
323 << ", distributed()=" << dist.distributed() << std::endl;
324 return stream;
325 }
326
327 //=============================================================================
328 /// Namespace for helper functions for LinearAlgebraDistributions
329 //=============================================================================
330 namespace LinearAlgebraDistributionHelpers
331 {
332 //===========================================================================
333 /// Takes a vector of LinearAlgebraDistribution objects and
334 /// concatenates them such that the nrow_local of the out_distribution
335 /// is the sum of the nrow_local of all the in_distributions and the number
336 /// of global rows of the out_distribution is the sum of the number of
337 /// global rows of all the in_distributions. This results in a permutation
338 /// of the rows in the out_distribution. Think of this in terms of
339 /// DoubleVectors, if we have DoubleVectors with distributions A and B,
340 /// distributed across two processors (p0 and p1), A: [a0] (on p0) B:
341 /// [b0] (on p0)
342 /// [a1] (on p1) [b1] (on P1),
343 ///
344 /// then the out_distribution is
345 /// [a0 (on p0)
346 /// b0] (on p0)
347 /// [a1 (on p1)
348 /// b1] (on p1),
349 ///
350 /// as opposed to
351 /// [a0 (on p0)
352 /// a1] (on p0)
353 /// [b0 (on p1)
354 /// b1] (on p1).
355 ///
356 /// Note (1): The out_distribution may not be uniformly distributed even
357 /// if the in_distributions are uniform distributions.
358 /// Try this out with two distributions of global rows 3 and 5, uniformly
359 /// distributed across two processors. Compare this against a distribution
360 /// of global row 8 distributed across two processors.
361 ///
362 /// Note (2): There is no equivalent function which takes a Vector of
363 /// LinearAlgebraDistribution objects (as opposed to pointers), there should
364 /// not be one since we do not want to invoke the assignment operator when
365 /// creating the Vector of LinearAlgebraDistribution objects.
366 //===========================================================================
370 {
371 // How many distributions are in in_distribution?
373
374#ifdef PARANOID
375
376 // Are any of the in_distribution pointers null?
377 // We do not want null pointers.
378 for (unsigned dist_i = 0; dist_i < ndistributions; dist_i++)
379 {
380 if (in_distribution_pt[dist_i] == 0)
381 {
382 std::ostringstream error_message;
383 error_message << "The pointer in_distribution_pt[" << dist_i
384 << "] is null.\n";
385 throw OomphLibError(error_message.str(),
388 }
389 }
390
391 ////////////////////////////////
392
393 // Check that all in distributions are built.
394 for (unsigned dist_i = 0; dist_i < ndistributions; dist_i++)
395 {
396 if (!in_distribution_pt[dist_i]->built())
397 {
398 std::ostringstream error_message;
399 error_message << "The in_distribution_pt[" << dist_i
400 << "] is not built.\n";
401 throw OomphLibError(error_message.str(),
404 }
405 }
406
407 ////////////////////////////////
408
409 // Check that all communicators to concatenate are the same
410 // by comparing all communicators against the first one.
412 *(in_distribution_pt[0]->communicator_pt());
413
414 for (unsigned dist_i = 0; dist_i < ndistributions; dist_i++)
415 {
416 // Get the Communicator for the current vector.
418 *(in_distribution_pt[dist_i]->communicator_pt());
419
420 if (!(first_comm == another_comm))
421 {
422 std::ostringstream error_message;
423 error_message << "The communicator in position " << dist_i << " is \n"
424 << "not the same as the first one.\n";
425 throw OomphLibError(error_message.str(),
428 }
429 }
430
431 ////////////////////////////////
432
433 // Ensure that all distributions are either distributed or not.
434 // This is because we use the distributed() function from the first
435 // distribution to indicate if the result distribution should be
436 // distributed or not.
437 bool first_distributed = in_distribution_pt[0]->distributed();
438 for (unsigned dist_i = 0; dist_i < ndistributions; dist_i++)
439 {
440 // Is the current distribution distributed?
441 bool another_distributed = in_distribution_pt[dist_i]->distributed();
443 {
444 std::ostringstream error_message;
445 error_message
446 << "The distribution in position " << dist_i << " has a different\n"
447 << "different distributed boolean than the distribution.\n";
448 throw OomphLibError(error_message.str(),
451 }
452 }
453
454 ////////////////////////////////
455
456 // Check that the out distribution is not built.
457 if (out_distribution.built())
458 {
459 std::ostringstream error_message;
460 error_message << "The out distribution is built.\n"
461 << "Please clear it.\n";
462 throw OomphLibError(error_message.str(),
465 }
466
467#endif
468
469 // Get the communicator pointer
470 const OomphCommunicator* const comm_pt =
471 in_distribution_pt[0]->communicator_pt();
472
473 // Number of processors
474 unsigned nproc = comm_pt->nproc();
475
476 // First determine the out_nrow_local and the out_nrow (the global nrow)
477 unsigned out_nrow_local = 0;
478 unsigned out_nrow = 0;
479 for (unsigned in_dist_i = 0; in_dist_i < ndistributions; in_dist_i++)
480 {
483 }
484
485 // Now calculate the first row for this processor. We need to know the
486 // out_nrow_local for all the other processors, MPI_Allgather must be
487 // used.
488 unsigned out_first_row = 0;
489
490 // Distributed case: We need to gather all the out_nrow_local from all
491 // processors, the out_first_row for this processors is the partial sum up
492 // of the out_nrow_local to my_rank.
493 bool distributed = in_distribution_pt[0]->distributed();
494 if (distributed)
495 {
496#ifdef OOMPH_HAS_MPI
497 // My rank
498 unsigned my_rank = comm_pt->my_rank();
499
500 unsigned* out_nrow_local_all = new unsigned[nproc];
502 1,
505 1,
507 comm_pt->mpi_comm());
508 for (unsigned proc_i = 0; proc_i < my_rank; proc_i++)
509 {
511 }
512 delete[] out_nrow_local_all;
513#endif
514 }
515 // Non-distributed case
516 else
517 {
518 out_first_row = 0;
519 }
520
521 // Build the distribution.
522 if (nproc == 1 || !distributed)
523 {
524 // Some ambiguity here -- on a single processor it doesn't really
525 // matter if we think of ourselves as distributed or not but this
526 // follows the pattern used elsewhere.
527 out_distribution.build(comm_pt, out_nrow, false);
528 }
529 else
530 {
531 out_distribution.build(
533 }
534 } // function concatenate
535
536 } // end of namespace LinearAlgebraDistributionHelpers
537
538} // namespace oomph
cstr elem_len * i
Definition cfortran.h:603
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
unsigned first_row() const
access function for the first row on this processor. If not distributed then this is just zero.
bool operator==(const LinearAlgebraDistribution &other_dist) const
== Operator
Vector< unsigned > Nrow_local
the number of local rows on the processor
void build(const OomphCommunicator *const comm_pt, const unsigned &first_row, const unsigned &nrow_local, const unsigned &nrow=0)
Sets the distribution. Takes first_row, nrow_local and nrow as arguments. If nrow is not provided or ...
OomphCommunicator * Comm_pt
the pointer to the MPI communicator object in this distribution
unsigned Nrow
the number of global rows
Vector< unsigned > First_row
the first row on this processor
bool Distributed
flag to indicate whether this distribution describes an object that is distributed over the processor...
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
void concatenate(const Vector< LinearAlgebraDistribution * > &in_distribution_pt, LinearAlgebraDistribution &out_distribution)
Takes a vector of LinearAlgebraDistribution objects and concatenates them such that the nrow_local of...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
std::ostream & operator<<(std::ostream &out, const DoubleVector &v)
output operator