OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
BinaryBalancer.hpp
Go to the documentation of this file.
1 // -*- C++ -*-
2 /***************************************************************************
3  *
4  * The IPPL Framework
5  *
6  * This program was prepared by PSI.
7  * All rights in the program are reserved by PSI.
8  * Neither PSI nor the author(s)
9  * makes any warranty, express or implied, or assumes any liability or
10  * responsibility for the use of this software
11  *
12  * Visit www.amas.web.psi for more details
13  *
14  ***************************************************************************/
15 
16 // -*- C++ -*-
17 /***************************************************************************
18  *
19  * The IPPL Framework
20  *
21  *
22  * Visit http://people.web.psi.ch/adelmann/ for more details
23  *
24  ***************************************************************************/
25 
26 // include files
29 #include "Field/BareField.h"
30 #include "Utility/PAssert.h"
31 
32 
34 
35 /*
36 
37  Implementation of BinaryBalancer.
38 
39  The general strategy is that you do log(P) splits on the domain. It
40  starts with the whole domain, does a reduction to find where to
41  split it, then does reductions on each of the resulting domains to
42  find where to split those, then reductions on those to split them,
43  and so on until it is done.
44 
45  Suppose you're on the n'th split, so there are 2**n domains figured
46  out so far, and after the split there will be 2**(n+1) splits. In
47  each of those 2**n domains you need to find
48 
49  a) The axis to split on. This is done by just finding the longest
50  axis in that domain.
51 
52  b) The location within that domain to make the split. This is done
53  by reducing the weights on all dimensions except the axis to be
54  split and finding the location within that array that puts half
55  the weight on each side.
56 
57  The reduction for b) is done is a scalable way. It is a parallel
58  reduction, and if there are 2**n domains being split, the reductions
59  are accumulated onto processors 0..2**n-1. Those processors
60  calculate the split locations and broadcast them to all the
61  processors.
62 
63  At every stage of the process all the processors know all the
64  domains. This is necessary because the weight array could be
65  distributed arbitrarily, so the reductions could involve any
66  processors.
67 
68  Nevertheless, the reductions are performed efficiently. Using
69  DomainMaps, only the processors that need to participate in a
70  reduction do participate.
71 
72 */
73 
75 
76 //
77 // Given an NDIndex<Dim> find the axis with the longest length, that is
78 // not SERIAL.
79 //
80 template <unsigned Dim>
81 static int
82 FindCutAxis(const NDIndex<Dim> &domain, const FieldLayout<Dim> &layout)
83 {
84 
85 
86 
87  // CutAxis will be the dimension to cut.
88  int cutAxis=-1;
89  // MaxLength will have the maximum length of any dimension.
90  unsigned int maxLength=0;
91  // Loop over dimension.
92  for (unsigned int d=0; d<Dim; ++d) {
93  if (layout.getDistribution(d) != SERIAL ||
94  layout.getRequestedDistribution(d) != SERIAL) {
95  // Check if this axis is longer than the current max.
96  unsigned int length = domain[d].length();
97  if ( maxLength < length ) {
98  // If so, remember it.
99  cutAxis = d;
100  maxLength = length;
101  }
102  }
103  }
104 
105  // Make sure we found one.
106  //PAssert_GE(cutAxis, 0);
107 
108  if(cutAxis<0)
109  throw BinaryRepartitionFailed();
110 
111  // Return the longest axis.
112  return cutAxis;
113 }
114 
115 
116 //
117 // Find the median point in a container.
118 // The first two arguments are begin and end iterators.
119 // The third is a dummy argument of type T, where the
120 // container is of objects of type T.
121 //
122 
123 template<class RandomIterator, class T>
124 static RandomIterator
125 FindMedian(int nprocs,RandomIterator begin, RandomIterator end, T)
126 {
127  // First find the total weight.
128  T w = 0;
129  // Use w to find T's name
130 
131 
132  // If we have only one processor, cut at the left.
133  if ( nprocs == 1 )
134  return begin;
135 
136  int lprocs = nprocs/2;
137  RandomIterator rp, median;
138  for (rp=begin; rp!=end; ++rp)
139  w += *rp;
140 
141  // If the total weight is zero, we need to do things specially.
142  if ( w==0 )
143  {
144  // The total weight is zero.
145  // Put about as much zero weight stuff on the left and the right.
146  median = begin + ((end-begin)*lprocs)/nprocs;
147  }
148  else
149  {
150  // The total weight is nonzero.
151  // Put equal amounts on the left and right processors.
152  T w2 = (w*lprocs)/nprocs;
153  // Find the point with half the weight to the left.
154  bool found = false;
155  w = T(0);
156  for (rp=begin; (rp!=end)&&(!found); ++rp) {
157  // Add current element to running total
158  w += *rp;
159  if (w>=w2) {
160  found = true;
161  if ( (w-w2) > (*rp/T(2)) )
162  median = rp;
163  else
164  median = (rp+1 != end) ? rp+1 : rp;
165  }
166  }
167  }
168  // Found it. Exit.
169  return median;
170 }
171 
173 
174 //
175 // Routines for doing reductions over all dimensions but one of a
176 // local BrickIterator.
177 //
178 // These will only work for 1, 2 and 3 dimensions right now.
179 // I'm sure there is a way to make this work efficiently
180 // for arbitrary dimension, but this works for now.
181 //
182 
183 // Reduce over a 3 dimensional BrickIterator
184 // given the axis not to reduce on
185 // and where in that dimension to reduce.
186 
187 static inline double
188 PerpReduce(BrickIterator<double,3>& data, int i, int cutAxis)
189 {
190  double r=0;
191  if (cutAxis==0)
192  {
193  int l1 = data.size(1);
194  int l2 = data.size(2);
195  if ( (l1>0) && (l2>0) )
196  for (int j2=0; j2<l2; ++j2)
197  for (int j1=0; j1<l1; ++j1)
198  r += data.offset(i,j1,j2);
199  }
200  else if (cutAxis==1)
201  {
202  int l0 = data.size(0);
203  int l2 = data.size(2);
204  if ( (l0>0) && (l2>0) )
205  for (int j2=0; j2<l2; ++j2)
206  for (int j0=0; j0<l0; ++j0)
207  r += data.offset(j0,i,j2);
208  }
209  else if (cutAxis==2)
210  {
211  int l0 = data.size(0);
212  int l1 = data.size(1);
213  if ( (l0>0) && (l1>0) )
214  for (int j1=0; j1<l1; ++j1)
215  for (int j0=0; j0<l0; ++j0)
216  r += data.offset(j0,j1,i);
217  }
218  return r;
219 }
220 
221 // Reduce over a 2 dimensional BrickIterator
222 // given the axis not to reduce on
223 // and where in that dimension to reduce.
224 
225 static inline double
226 PerpReduce(BrickIterator<double,2>& data, int i, int cutAxis)
227 {
228  double r=0;
229  if (cutAxis==0)
230  {
231  int length = data.size(1);
232  for (int j=0; j<length; ++j)
233  r += data.offset(i,j);
234  }
235  else
236  {
237  int length = data.size(0);
238  for (int j=0; j<length; ++j)
239  r += data.offset(j,i);
240  }
241  return r;
242 }
243 
244 //
245 // Reduce over a 1 dimensional BrickIterator
246 // given the axis not to reduce on
247 // and where in that dimension to reduce.
248 //
249 
250 static inline double
251 PerpReduce(BrickIterator<double,1>& data, int i, int )
252 {
253  return data.offset(i);
254 }
255 
256 //
257 // Reduce over all the dimensions but one of a brick iterator.
258 // Put the results in an array of doubles.
259 //
260 
261 template<unsigned Dim>
262 static void
263 LocalReduce(double *reduced, int cutAxis, BrickIterator<double,Dim> data)
264 {
265 
266 
267 
268  int length = data.size(cutAxis);
269  for (int i=0; i<length; ++i)
270  reduced[i] = PerpReduce(data,i,cutAxis);
271 }
272 
274 
275 //
276 // For each domain, do the local reduction of
277 // data from weights, and send that to the node
278 // that is accumulating stuff for that domain.
279 //
280 // The local reductions take place all across the machine.
281 // The reductions for each domain are finished on a single processor.
282 // Each of those final reductions are on different processors.
283 //
284 
285 template<class IndexIterator, unsigned Dim>
286 static void
287 SendReduce(IndexIterator domainsBegin, IndexIterator domainsEnd,
288  BareField<double,Dim>& weights, int tag)
289 {
290 
291  // Buffers to store up domains and blocks of reduced data.
292  std::vector<double*> reducedBuffer;
293  std::vector<Index> domainBuffer;
294  // Loop over all of the domains. Keep a counter of which one you're on.
295  int di;
296  IndexIterator dp;
297  /*out << "SendReduce, ndomains=" << domainsEnd-domainsBegin << endl;*/
298  for (dp=domainsBegin, di=0; dp!=domainsEnd; ++dp, ++di)
299  {
300  /*out << "SendReduce, domain=" << *dp << endl;*/
301  // Find the dimension we'll be cutting on.
302  // We'll reduce in the dimensions perpendicular to this.
303  int cutAxis = FindCutAxis(*dp, weights.getLayout());
304  // Find the LFields on this processor that touch this domain.
306  for (lf_p=weights.begin_if(); lf_p != weights.end_if(); ++lf_p)
307  if ( (*dp).touches( (*lf_p).second->getOwned() ) )
308  {
309  // Find the intersection with this LField.
310  NDIndex<Dim> intersection =
311  (*dp).intersect( (*lf_p).second->getOwned() );
312  // Allocate the accumulation buffer.
313  int length = intersection[cutAxis].length();
314  double *reduced = new double[length];
315  // Reduce into the local buffer.
316  /*out << "LocalReduce " << intersection << endl;*/
317  LocalReduce(reduced,cutAxis,(*lf_p).second->begin(intersection));
318  // Save the domain and the data.
319  reducedBuffer.push_back(reduced);
320  domainBuffer.push_back(intersection[cutAxis]);
321  }
322 
323  // If we found any hits, send them out.
324  int nrdomains = reducedBuffer.size();
325  /*out << "nrdomains=" << nrdomains << endl;*/
326  if ( nrdomains>0 )
327  {
328  // Build a message to hold everything for this domain.
329  Message *mess = new Message;
330  // The number of reduced domains is the first thing in the message.
331  mess->put(nrdomains);
332  // Loop over the reduced domains, storing in the message each time.
333  std::vector<Index>::iterator dbp = domainBuffer.begin();
334  std::vector<double*>::iterator rbp = reducedBuffer.begin();
335  for (int i=0; i<nrdomains; ++i, ++dbp, ++rbp)
336  {
337  // First store the domain.
338  /*out << "putMessage " << *dbp << endl;*/
339  putMessage(*mess,*dbp);
340  // Then the reduced data using begin/end iterators.
341  // Tell the message to delete the memory when it is done.
342  double *p = *rbp;
343  mess->setCopy(false).setDelete(true).put(p,p+(*dbp).length());
344  }
345  // Send the message to proc di.
346  DEBUGMSG("Comm->Send to Node " << di << ", tag=" << tag << endl);
347  Ippl::Comm->send(mess, di, tag);
348  }
349  // Clear out the buffers.
350  domainBuffer.erase( domainBuffer.begin(), domainBuffer.end() );
351  reducedBuffer.erase( reducedBuffer.begin(), reducedBuffer.end() );
352  /*out << "Bottom of SendReduce loop" << endl;*/
353  }
354 }
355 
357 
358 //
359 // Receive the messages with reduced data sent out in SendReduce.
360 // Finish the reduction.
361 // Return begin and end iterators for the reduced data.
362 //
363 
364 template<unsigned Dim>
365 static void
366 ReceiveReduce(NDIndex<Dim>& domain, BareField<double,Dim>& weights,
367  int reduce_tag, int nprocs,
368  int& cutLoc, int& cutAxis)
369 {
370 
371 
372  // Build a place to accumulate the reduced data.
373  cutAxis = FindCutAxis(domain, weights.getLayout());
374  /*out << "ReceiveReduce, cutAxis=" << cutAxis << endl;*/
375  int i, length = domain[cutAxis].length();
376  int offset = domain[cutAxis].first();
377  std::vector<double> reduced(length);
378  std::vector<double> subReduced(length);
379  for (i=0; i<length; ++i)
380  reduced[i] = 0;
381 
382  // Build a count of the number of messages to expect.
383  // We get *one message* from each node that has a touch.
384  int expected = 0;
385  int nodes = Ippl::getNodes();
386  int mynode = Ippl::myNode();
387  bool* found_touch = new bool[nodes];
388  for (i=0; i<nodes; ++i) found_touch[i] = false;
389  // First look in the local vnodes of weights.
390  typename BareField<double,Dim>::iterator_if lf_p, lf_end = weights.end_if();
391  for (lf_p = weights.begin_if();
392  lf_p != lf_end && !(found_touch[mynode]); ++lf_p) {
393  // Expect a message if it touches.
394  if ( (*lf_p).second->getOwned().touches(domain) )
395  found_touch[mynode] = true;
396  }
397  // Now look in the remote parts of weights.
399  // Get the range of remote vnodes that touch domain.
400  typename FieldLayout<Dim>::touch_range_dv range =
401  weights.getLayout().touch_range_rdv( domain );
402  // Record the processors who have touches
403  for (rf_p = range.first; rf_p != range.second ; ++rf_p) {
404  int owner = (*((*rf_p).second)).getNode();
405  found_touch[owner] = true;
406  }
407  // now just count up the number of messages to receive
408  for (i=0; i<nodes; ++i)
409  if (found_touch[i]) expected++;
410  delete [] found_touch;
411 
412  DEBUGMSG("ReceiveReduce, msgs expected=" << expected << endl);
413  // Receive messages until we're done.
414  while ( --expected >= 0 )
415  {
416  // Receive a message.
417  int any_node = COMM_ANY_NODE;
418  Message *mess = Ippl::Comm->receive_block(any_node,reduce_tag);
419  PAssert(mess);
420  DEBUGMSG("ReceiveReduce: Comm->Receive from Node " << any_node << ", tag=" << reduce_tag << endl);
421  // Loop over all the domains in this message.
422  int received_domains = 0;
423  mess->get(received_domains);
424  while ( --received_domains>=0 )
425  {
426  // Get the domain for the next part.
427  Index rdomain;
428  getMessage( *mess, rdomain );
429  /*out << "ReceiveReduce, rdomain=" << rdomain << endl;*/
430  // Get the incoming reduced data.
431  int rfirst = rdomain.first() - offset;
432  mess->get(subReduced[rfirst]);
433  // Accumulate it with the rest.
434  int rlast = rdomain.last() - offset;
435  for (int i=rfirst; i<=rlast; ++i)
436  reduced[i] += subReduced[i];
437  }
438  // Delete the message, we're done with it
439  delete mess;
440  }
441 
442  // Get the median.
443  cutLoc =
444  FindMedian(nprocs,reduced.begin(),reduced.begin()+length,double())
445  -reduced.begin() + domain[cutAxis].first();
446  /*out << "ReceiveReduce, cutLoc=" << cutLoc << endl;*/
447 }
448 
450 
451 //
452 // Given the location and axis of the cut,
453 // Broadcast to everybody.
454 //
455 
456 inline void
457 BcastCuts(int cutLoc, int cutAxis, int bcast_tag)
458 {
459 
460 
461  // Make a message.
462  Message *mess = new Message();
463  // Add the data to it.
464  DEBUGMSG("Broadcast cutLoc=" << cutLoc << ", cutAxis=" << cutAxis << endl);
465  mess->put(cutLoc);
466  mess->put(cutAxis);
467  // Send it out.
468  Ippl::Comm->broadcast_all(mess,bcast_tag);
469 }
470 
472 
473 //
474 // Receive the broadcast cuts.
475 // Cut up each of the domains using the cuts.
476 //
477 
478 template<unsigned Dim>
479 static void
480 ReceiveCuts(std::vector< NDIndex<Dim> > &domains,
481  std::vector< int >& nprocs,
482  int bcast_tag)
483 {
484 
485 
486 
487  // Make a container to hold the split domains.
488  int nDomains = domains.size();
489  std::vector< NDIndex<Dim> > cutDomains(nDomains*2);
490  std::vector<int> cutProcs(std::vector<int>::size_type(nDomains*2));
491 
492  // Everybody receives the broadcasts.
493  // There will be one for each domain in the list.
494  for (int expected = 0; expected < nDomains; ++expected)
495  {
496  // Receive each broadcast.
497  // The processor number will correspond to the location
498  // in the domains vector.
499  int whichDomain = COMM_ANY_NODE;
500  int cutLocation = 0, cutAxis = 0;
501  Message *mess = Ippl::Comm->receive_block(whichDomain,bcast_tag);
502  PAssert(mess);
503  DEBUGMSG("ReceiveCuts: received bcast " << expected << endl);
504  mess->get(cutLocation);
505  mess->get(cutAxis);
506  delete mess;
507 
508  // Split this domain.
509  const NDIndex<Dim>& domain = domains[whichDomain];
510  NDIndex<Dim>& left = cutDomains[ whichDomain*2 ];
511  NDIndex<Dim>& right = cutDomains[ whichDomain*2+1 ];
512  // Build the left and right domains.
513  left = domain ;
514  right = domain ;
515  /*out << "Build indexes from : "
516  << domain[cutAxis].first() << " "
517  << cutLocation<< " "
518  << domain[cutAxis].last()<< " "
519  << endl;*/
520  left[ cutAxis ] = Index( domain[cutAxis].first(), cutLocation-1 );
521  right[ cutAxis ] = Index( cutLocation, domain[cutAxis].last() );
522 
523  int procs = nprocs[whichDomain];
524  cutProcs[ whichDomain*2 ] = procs/2;
525  cutProcs[ whichDomain*2+1 ] = procs - procs/2;
526  }
527 
528  // Put the domains you've just built into the input containers.
529  // Strip out the domains with no processors assigned.
530  domains.clear();
531  nprocs.clear();
532  PAssert_EQ(cutProcs.size(), cutDomains.size());
533  for (unsigned int i=0; i<cutProcs.size(); ++i)
534  {
535  if ( cutProcs[i] != 0 )
536  {
537  domains.push_back(cutDomains[i]);
538  nprocs.push_back(cutProcs[i]);
539  }
540  else
541  {
542  PAssert_EQ(cutDomains[i].size(), 0);
543  }
544  }
545 }
546 
548 
549 //
550 // Sweep through a list of domains, splitting each one
551 // according to the weights in a BareField.
552 //
553 
554 template<unsigned Dim>
555 static void
556 CutEach(std::vector< NDIndex<Dim> >& domains,
557  std::vector< int >& nprocs,
558  BareField<double,Dim>& weights)
559 {
560 
561  // Get tags for the reduction and the broadcast.
562  int reduce_tag = Ippl::Comm->next_tag( F_REDUCE_PERP_TAG , F_TAG_CYCLE );
563  int bcast_tag = Ippl::Comm->next_tag( F_REDUCE_PERP_TAG , F_TAG_CYCLE );
564  /*out << "reduce_tag=" << reduce_tag << endl;*/
565  /*out << "bcast_tag=" << bcast_tag << endl;*/
566 
567  // Do the sends for the reduces.
568  DEBUGMSG("Do SendReduce" << endl);
569  SendReduce(domains.begin(),domains.end(),weights,reduce_tag);
570  DEBUGMSG("Did SendReduce" << endl);
571 
572  // On the appropriate processors, receive the data for the reduce,
573  // and broadcast the cuts.
574  unsigned int mynode = Ippl::Comm->myNode();
575  if ( mynode < domains.size() )
576  {
577  // Receive partially reduced data, finish the reduction, find the median.
578  int cutAxis, cutLoc;
579  DEBUGMSG("Do ReceiveReduce" << endl);
580  ReceiveReduce(domains[mynode],weights,reduce_tag,
581  nprocs[mynode],cutLoc,cutAxis);
582  DEBUGMSG("Did ReceiveReduce" << endl);
583  // Broadcast those cuts out to everybody.
584  DEBUGMSG("Do BcastCuts" << endl);
585  BcastCuts(cutLoc,cutAxis,bcast_tag);
586  DEBUGMSG("Did BcastCuts" << endl);
587  }
588 
589  // Receive the broadcast cuts and slice up the domains.
590  DEBUGMSG("Do ReceiveCuts" << endl);
591  ReceiveCuts(domains,nprocs,bcast_tag);
592  DEBUGMSG("Did ReceiveCuts" << endl);
593 }
594 
596 
597 template<unsigned Dim>
600 {
601 // Build a list of domains as we go.
602  std::vector< NDIndex<Dim> > domains; // used by TAU_TYPE_STRING
603  std::vector<int> procs;
604 
605  /*out << "Starting CalcBinaryRepartition, outstanding msgs="
606  << Ippl::Comm->getReceived()
607  << endl;*/
608 
609  // Get the processors we'll be dealing with.
610  int nprocs = Ippl::Comm->getNodes();
611  int myproc = Ippl::Comm->myNode();
612  domains.reserve(nprocs);
613  procs.reserve(nprocs);
614  // Start the list with just the top level domain.
615  domains.push_back( layout.getDomain() );
616  procs.push_back( nprocs );
617 
618  // mprocs is the max number of procs assigned to a domain.
619  int mprocs=nprocs;
620 
621  // Loop as long as some domain has more than one proc assigned to it.
622  while ( mprocs>1 )
623  {
624  // Cut all the domains in half.
625  DEBUGMSG("Do Cut " << mprocs << endl);
626  CutEach(domains,procs,weights);
627  DEBUGMSG("Did Cut " << mprocs << endl);
628 
629  // Find the max number of procs assigned to a domain.
630  mprocs = 0;
631  for (unsigned int i=0; i<procs.size(); ++i)
632  if (mprocs<procs[i]) mprocs = procs[i];
633  }
634  // Return the domain on this processor.
635 
636 
637  //seriously dirty fix
638  typename std::vector< NDIndex<Dim> >::iterator i;
639 
640  bool degenerated = false;
641 
642  for(i = domains.begin();i!=domains.end();++i)
643  {
644  for(unsigned int d = 0;d<Dim;++d)
645  if((*i)[d].first() == (*i)[d].last())
646  {
647  degenerated = true;
648  break;
649  }
650  if(degenerated)
651  break;
652  }
653 
654  if(!degenerated)
655  return domains.begin()[myproc];
656  else
657  {
658  throw BinaryRepartitionFailed();
659  }
660 
661 }
662 
664 
665 
666 /***************************************************************************
667  * $RCSfile: BinaryBalancer.cpp,v $ $Author: adelmann $
668  * $Revision: 1.1.1.1 $ $Date: 2003/01/23 07:40:27 $
669  * IPPL_VERSION_ID: $Id: BinaryBalancer.cpp,v 1.1.1.1 2003/01/23 07:40:27 adelmann Exp $
670  ***************************************************************************/
static int getNodes()
Definition: IpplInfo.cpp:773
Layout_t & getLayout() const
Definition: BareField.h:130
ac_id_larray::iterator iterator_if
Definition: BareField.h:91
Definition: rbendmap.h:8
int myNode() const
Definition: Communicate.h:155
touch_range_dv touch_range_rdv(const NDIndex< Dim > &domain, const GuardCellSizes< Dim > &gc=gc0()) const
Definition: FieldLayout.h:780
T & offset(int i) const
const int COMM_ANY_NODE
Definition: Communicate.h:40
static int myNode()
Definition: IpplInfo.cpp:794
void BcastCuts(int cutLoc, int cutAxis, int bcast_tag)
int next_tag(int t, int s=1000)
Definition: TagMaker.h:43
iterator_if end_if()
Definition: BareField.h:100
#define PAssert_EQ(a, b)
Definition: PAssert.h:119
Definition: Index.h:236
int last() const
Definition: IndexInlines.h:136
e_dim_tag getRequestedDistribution(unsigned int d) const
Definition: FieldLayout.h:405
int size(unsigned d) const
Definition: BrickIterator.h:48
#define F_REDUCE_PERP_TAG
Definition: Tags.h:49
Definition: FFT.h:30
Message & get(const T &cval)
Definition: Message.h:484
Message & put(const T &val)
Definition: Message.h:414
NDIndex< Dim > CalcBinaryRepartition(FieldLayout< Dim > &, BareField< double, Dim > &)
void getMessage(Message &m, T &t)
Definition: Message.h:580
e_dim_tag getDistribution(unsigned int d) const
Definition: FieldLayout.h:396
NDIndex< Dim > intersect(const NDIndex< Dim > &) const
int first() const
Definition: IndexInlines.h:116
#define PAssert(c)
Definition: PAssert.h:117
std::string::iterator iterator
Definition: MSLang.h:16
const unsigned Dim
void putMessage(Message &m, const T &t)
Definition: Message.h:557
iterator_if begin_if()
Definition: BareField.h:99
Message * receive_block(int &node, int &tag)
Message & setDelete(const bool c)
Definition: Message.h:339
#define DEBUGMSG(msg)
Definition: IpplInfo.h:405
static Communicate * Comm
Definition: IpplInfo.h:93
Message & setCopy(const bool c)
Definition: Message.h:327
bool send(Message *, int node, int tag, bool delmsg=true)
std::pair< touch_iterator_dv, touch_iterator_dv > touch_range_dv
Definition: FieldLayout.h:77
const NDIndex< Dim > & getDomain() const
Definition: FieldLayout.h:325
virtual int broadcast_all(Message *, int)
int getNodes() const
Definition: Communicate.h:143
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
#define F_TAG_CYCLE
Definition: Tags.h:53