double_vector_with_halo.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-2023 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 
28 namespace oomph
29 {
30  //============================================================================
31  /// Constructor that sets up the required information communicating
32  /// between all processors. Requires two "all to all" communications.
33  /// Arguments are the distribution of the DoubleVector and a
34  /// Vector of global unknowns required on this processor.
35  //===========================================================================
37  LinearAlgebraDistribution* const& dist_pt,
38  const Vector<unsigned>& required_global_eqn)
39  : Distribution_pt(dist_pt)
40  {
41 #ifdef OOMPH_HAS_MPI
42  // Only bother to do anything if the vector is distributed
43  if (dist_pt->distributed())
44  {
45  // First create temporary maps for halo requests.
46  // Using the map structure ensures that the data will be sorted
47  // into processor order: the order of the unsigned integer key
48 
49  // These are the halo requests indexed by master processor and the
50  // local equation number that is to be haloed on that processor
51  std::map<unsigned, Vector<unsigned>> to_be_haloed;
52  // These are the halo requests indexed by master processor and the
53  // index in the additional storage
54  // that corresponds to the halo data on this processor
55  std::map<unsigned, Vector<unsigned>> halo_entries;
56 
57  // Find rank of current processor
58  const unsigned my_rank =
59  static_cast<int>(dist_pt->communicator_pt()->my_rank());
60  // Loop over the desired equations (on this processor)
61  // and work out whether we need to halo them according to the
62  // given distribution
63  const unsigned n_global_eqn = required_global_eqn.size();
64  // Index for the locally stored halo values
65  unsigned index = 0;
66  for (unsigned n = 0; n < n_global_eqn; n++)
67  {
68  // Cache the required GLOBAL equation number
69  const unsigned i_global = required_global_eqn[n];
70  // Where is it stored?
71  unsigned rank_of_global = dist_pt->rank_of_global_row(i_global);
72  // If the equation is not stored locally then
73  // populate the two maps
74  if (my_rank != rank_of_global)
75  {
76  // Work out the local entry on the appropriate processor
77  unsigned i_local = i_global - dist_pt->first_row(rank_of_global);
78  // Mark the local storage index as halo with rank_of_global as master
79  halo_entries[rank_of_global].push_back(index);
80  // Mark the local equation of the rank_of_global as to be
81  // haloed
82  to_be_haloed[rank_of_global].push_back(i_local);
83  // Store the local index corresponding to the global equation
84  Local_index[i_global] = index;
85  // Increment the index
86  ++index;
87  }
88  }
89 
90  // We now need to tell the other processors which of their data are
91  // haloed on this processor
92 
93  // First find out how many processors there are!
94  const int n_proc = dist_pt->communicator_pt()->nproc();
95 
96  // Setup storage for number of data to be sent to each processor
97  Vector<int> send_n(n_proc, 0);
98  Vector<int> send_displacement(n_proc, 0);
99  int send_data_count = 0;
100 
101  // Iterate over the entries in the map
102  // This will be in rank order because of the ordering of the map
103  for (std::map<unsigned, Vector<unsigned>>::iterator it =
104  to_be_haloed.begin();
105  it != to_be_haloed.end();
106  ++it)
107  {
108  const unsigned rank = it->first;
109  const unsigned size_ = it->second.size();
110  // The displacement is the current number of data
111  send_displacement[rank] = send_data_count;
112  // The number to send is just the size of the array
113  send_n[rank] = static_cast<int>(size_);
114  send_data_count += size_;
115  }
116 
117  // Now send the number of haloed entries from every processor
118  // to every processor
119 
120  // Receive the data directly into the storage for haloed daa
121  Haloed_n.resize(n_proc, 0);
122  MPI_Alltoall(&send_n[0],
123  1,
124  MPI_INT,
125  &Haloed_n[0],
126  1,
127  MPI_INT,
128  dist_pt->communicator_pt()->mpi_comm());
129 
130  // Prepare the data to be sent
131  // Always resize to at least one
132  if (send_data_count == 0)
133  {
134  ++send_data_count;
135  }
136  Vector<unsigned> send_data(send_data_count);
137  // Iterate over the entries in the map
138  unsigned count = 0;
139  for (std::map<unsigned, Vector<unsigned>>::iterator it =
140  to_be_haloed.begin();
141  it != to_be_haloed.end();
142  ++it)
143  {
144  // Iterate over the vector
145  for (Vector<unsigned>::iterator it2 = it->second.begin();
146  it2 != it->second.end();
147  ++it2)
148  {
149  send_data[count] = (*it2);
150  ++count;
151  }
152  }
153 
154  // Prepare the data to be received,
155  // Again this can go directly into Haloed storage
156  int receive_data_count = 0;
157  Haloed_displacement.resize(n_proc);
158  for (int d = 0; d < n_proc; d++)
159  {
160  // The displacement is the amount of data received so far
161  Haloed_displacement[d] = receive_data_count;
162  receive_data_count += Haloed_n[d];
163  }
164 
165  // Now resize the receive buffer
166  // Always make sure that it has size of at least one
167  if (receive_data_count == 0)
168  {
169  ++receive_data_count;
170  }
171  Haloed_eqns.resize(receive_data_count);
172  // Send the data between all the processors
173  MPI_Alltoallv(&send_data[0],
174  &send_n[0],
175  &send_displacement[0],
176  MPI_UNSIGNED,
177  &Haloed_eqns[0],
178  &Haloed_n[0],
180  MPI_UNSIGNED,
181  dist_pt->communicator_pt()->mpi_comm());
182 
183  // Finally, we translate the map of halo entries into the permanent
184  // storage
185  Halo_n.resize(n_proc, 0);
186  Halo_displacement.resize(n_proc, 0);
187 
188  // Loop over all the entries in the map
189  unsigned receive_haloed_count = 0;
190  for (int d = 0; d < n_proc; d++)
191  {
192  // Pointer to the map entry
193  std::map<unsigned, Vector<unsigned>>::iterator it =
194  halo_entries.find(d);
195  // If we don't have it in the map, skip
196  if (it == halo_entries.end())
197  {
198  Halo_displacement[d] = receive_haloed_count;
199  Halo_n[d] = 0;
200  }
201  else
202  {
203  Halo_displacement[d] = receive_haloed_count;
204  const int size_ = it->second.size();
205  Halo_n[d] = size_;
206  // Resize the equations to be sent
207  Halo_eqns.resize(receive_haloed_count + size_);
208  for (int i = 0; i < size_; i++)
209  {
210  Halo_eqns[receive_haloed_count + i] = it->second[i];
211  }
212  receive_haloed_count += size_;
213  }
214  }
215  }
216 #endif
217  }
218 
219  //=====================================================================
220  /// Function that sets up a vector of pointers to halo
221  /// data, index using the scheme in Local_index. The first arguement
222  /// is a map of pointers to all halo data index by the global equation
223  /// number
224  //====================================================================
226  const std::map<unsigned, double*>& halo_data_pt,
227  Vector<double*>& halo_dof_pt)
228  {
229  // How many entries are there in the map
230  unsigned n_halo = Local_index.size();
231  // Resize the vector
232  halo_dof_pt.resize(n_halo);
233 
234  // Loop over all the entries in the map
235  for (std::map<unsigned, unsigned>::iterator it = Local_index.begin();
236  it != Local_index.end();
237  ++it)
238  {
239  // Find the pointer in the halo_data_pt map
240  std::map<unsigned, double*>::const_iterator it2 =
241  halo_data_pt.find(it->first);
242  // Did we find it
243  if (it2 != halo_data_pt.end())
244  {
245  // Now set the entry
246  halo_dof_pt[it->second] = it2->second;
247  }
248  else
249  {
250  std::ostringstream error_stream;
251  error_stream << "Global equation " << it->first
252  << " reqired as halo is not stored in halo_data_pt\n";
253 
254  throw OomphLibError(
255  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
256  }
257  }
258  }
259 
260  //------------------------------------------------------------------
261  // Member functions for the DoubleVectorWithHaloEntries
262  //-------------------------------------------------------------------
263 
264 
265  //=========================================================================
266  /// Synchronise the halo data within the vector. This requires one
267  /// "all to all" communnication.
268  //====================================================================
270  {
271 #ifdef OOMPH_HAS_MPI
272  // Only need to do anything if the DoubleVector is distributed
273  if (this->distributed())
274  {
275  // Read out the number of entries to send
276  const unsigned n_send = Halo_scheme_pt->Haloed_eqns.size();
277  Vector<double> send_data(n_send);
278  // Read out the data values
279  for (unsigned i = 0; i < n_send; i++)
280  {
281  send_data[i] = (*this)[Halo_scheme_pt->Haloed_eqns[i]];
282  }
283 
284  // Read out the number of entries to receive
285  const unsigned n_receive = Halo_scheme_pt->Halo_eqns.size();
286  Vector<double> receive_data(n_receive);
287 
288  // Make sure that the send and receive data have size at least one
289  if (n_send == 0)
290  {
291  send_data.resize(1);
292  }
293  if (n_receive == 0)
294  {
295  receive_data.resize(1);
296  }
297  // Communicate
298  MPI_Alltoallv(&send_data[0],
301  MPI_DOUBLE,
302  &receive_data[0],
303  &Halo_scheme_pt->Halo_n[0],
305  MPI_DOUBLE,
306  this->distribution_pt()->communicator_pt()->mpi_comm());
307 
308 
309  // Now I need simply to update my local values
310  for (unsigned i = 0; i < n_receive; i++)
311  {
312  Halo_value[Halo_scheme_pt->Halo_eqns[i]] = receive_data[i];
313  }
314  }
315 #endif
316  }
317 
318  //=========================================================================
319  /// Gather all ther data from multiple processors and sum the result
320  /// which will be stored in the master copy and then synchronised to
321  /// all copies. This requires two "all to all" communications
322  //====================================================================
324  {
325 #ifdef OOMPH_HAS_MPI
326  // Only need to do anything if the DoubleVector is distributed
327  if (this->distributed())
328  {
329  // Send the Halo entries to the master processor
330  const unsigned n_send = Halo_scheme_pt->Halo_eqns.size();
331  Vector<double> send_data(n_send);
332  // Read out the data values
333  for (unsigned i = 0; i < n_send; i++)
334  {
335  send_data[i] = Halo_value[Halo_scheme_pt->Halo_eqns[i]];
336  }
337 
338  // Read out the number of entries to receive
339  const unsigned n_receive = Halo_scheme_pt->Haloed_eqns.size();
340  Vector<double> receive_data(n_receive);
341 
342  // Make sure that the send and receive data have size at least one
343  if (n_send == 0)
344  {
345  send_data.resize(1);
346  }
347  if (n_receive == 0)
348  {
349  receive_data.resize(1);
350  }
351  // Communicate
352  MPI_Alltoallv(&send_data[0],
353  &Halo_scheme_pt->Halo_n[0],
355  MPI_DOUBLE,
356  &receive_data[0],
359  MPI_DOUBLE,
360  this->distribution_pt()->communicator_pt()->mpi_comm());
361 
362 
363  // Now I need simply to update and sum my local values
364  for (unsigned i = 0; i < n_receive; i++)
365  {
366  (*this)[Halo_scheme_pt->Haloed_eqns[i]] += receive_data[i];
367  }
368 
369  // Then synchronise
370  this->synchronise();
371  }
372 #endif
373  }
374 
375 
376  //===================================================================
377  /// Construct the halo scheme and storage for the halo data
378  //=====================================================================
380  DoubleVectorHaloScheme* const& halo_scheme_pt)
381  {
383 
384  if (Halo_scheme_pt != 0)
385  {
386  // Need to set up the halo data
387  unsigned n_halo_data = halo_scheme_pt->Local_index.size();
388 
389  // Resize the halo storage
390  Halo_value.resize(n_halo_data);
391 
392  // Now let's get the initial values from the other processors
393  this->synchronise();
394  }
395  }
396 
397 
398 } // namespace oomph
cstr elem_len * i
Definition: cfortran.h:603
bool distributed() const
distribution is serial or distributed
A class that stores the halo/haloed entries required when using a DoubleVectorWithHaloEntries....
Vector< int > Haloed_n
Storage for the number of haloed entries to be sent to each processor.
Vector< int > Halo_displacement
Storage for the offsets of the processor data in the receive buffer.
Vector< int > Halo_n
Storage for the number of entries to be received from each other processor.
std::map< unsigned, unsigned > Local_index
Storage for the translation scheme from global unknown to local index in the additional storage vecto...
Vector< int > Haloed_displacement
Storage for the offsets of the haloed entries for each processor in the packed Haloed_eqns array.
void setup_halo_dofs(const std::map< unsigned, double * > &halo_data_pt, Vector< double * > &halo_dof_pt)
Function that sets up a vector of pointers to halo data, index using the scheme in Local_index.
Vector< unsigned > Haloed_eqns
The haloed entries that will be sent in a format compatible with MPI_Alltoallv i.e....
DoubleVectorHaloScheme(LinearAlgebraDistribution *const &dist_pt, const Vector< unsigned > &required_global_eqn)
Constructor that sets up the required information communicating between all processors....
Vector< unsigned > Halo_eqns
Storage for all the entries that are to be received from other processors (received_from_proc0,...
void build_halo_scheme(DoubleVectorHaloScheme *const &halo_scheme_pt)
Construct the halo scheme and storage for the halo data.
DoubleVectorHaloScheme * Halo_scheme_pt
Pointer to the lookup scheme that stores information about on which processor the required informatio...
void synchronise()
Synchronise the halo data.
void sum_all_halo_and_haloed_values()
Sum all the data, store in the master (haloed) data and then synchronise.
DoubleVectorHaloScheme *& halo_scheme_pt()
Access function for halo scheme.
Vector< double > Halo_value
Vector of the halo values.
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
bool distributed() const
access function to the distributed - indicates whether the distribution is serial or distributed
unsigned first_row() const
access function for the first row on this processor. If not distributed then this is just zero.
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
unsigned rank_of_global_row(const unsigned i) const
return the processor rank of the global row number i
An OomphLibError object which should be thrown when an run-time error is encountered....
//////////////////////////////////////////////////////////////////// ////////////////////////////////...