OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
ParticleSpatialLayout.h
Go to the documentation of this file.
1// -*- C++ -*-
2/***************************************************************************
3 *
4 * The IPPL Framework
5 *
6 ***************************************************************************/
7
8#ifndef PARTICLE_SPATIAL_LAYOUT_H
9#define PARTICLE_SPATIAL_LAYOUT_H
10
11/*
12 * ParticleSpatialLayout - particle layout based on spatial decomposition.
13 *
14 * This is a specialized version of ParticleLayout, which places particles
15 * on processors based on their spatial location relative to a fixed grid.
16 * In particular, this can maintain particles on processors based on a
17 * specified FieldLayout or RegionLayout, so that particles are always on
18 * the same node as the node containing the Field region to which they are
19 * local. This may also be used if there is no associated Field at all,
20 * in which case a grid is selected based on an even distribution of
21 * particles among processors.
22 */
23
24// include files
27#include "Region/RegionLayout.h"
28#include "Message/Message.h"
29#include "Message/Format.h"
30#include "Message/MsgBuffer.h"
33
34#include <cstddef>
35
36#include <functional>
37#include <iostream>
38#include <map>
39#include <memory>
40#include <vector>
41
43
44#include <mpi.h>
45
46// forward declarations
47class UserList;
48template <class T> class ParticleAttrib;
49template <unsigned Dim, class T> class UniformCartesian;
50template <class T, unsigned Dim, class Mesh, class CachingPolicy> class ParticleSpatialLayout;
51template <class T, unsigned Dim, class Mesh, class CachingPolicy>
52std::ostream& operator<<(std::ostream&, const ParticleSpatialLayout<T,Dim,Mesh,CachingPolicy>&);
53
54// ParticleSpatialLayout class definition. Template parameters are the type
55// and dimension of the ParticlePos object used for the particles. The
56// dimension of the position must match the dimension of the FieldLayout
57// object used in this particle layout, if any.
58// Optional template parameter for the mesh type
59template < class T, unsigned Dim, class Mesh=UniformCartesian<Dim,T>, class CachingPolicy=BoxParticleCachingPolicy<T,Dim,Mesh > >
61 public FieldLayoutUser, public CachingPolicy
62{
63 /*
64 Enable cashing of particles. The size is
65 given in multiples of the mesh size in each dimension.
66 */
67
68public:
69 // pair iterator definition ... this layout does not allow for pairlists
70 typedef int pair_t;
75
76 // type of attributes this layout should use for position and ID
80
81public:
82 // constructor: The Field layout to which we match our particle's
83 // locations.
85
86 // constructor: this one also takes a Mesh
88
89 // a similar constructor, but this one takes a RegionLayout.
91
92 // a default constructor ... in this case, no layout will
93 // be assumed by this class. A layout may be given later via the
94 // 'setLayout' method, either as a FieldLayout or as a RegionLayout.
96
97 // destructor
99
100 //
101 // spatial decomposition layout information
102 //
103
104 // retrieve a reference to the FieldLayout object in use. This may be used,
105 // e.g., to construct a Field with the same layout as the Particles. Note
106 // that if this object was constructed by providing a RegionLayout in the
107 // constructor, then this generated FieldLayout will not necessarily match
108 // up with the Region (it will be offset by some amount). But, if this
109 // object was either 1) created with a FieldLayout to begin with, or 2)
110 // created with no layout, and one was generated internally, then the
111 // returned FieldLayout will match and can be used to make new Fields or
112 // Particles.
114 {
115 return RLayout.getFieldLayout();
116 }
117
118 // retrieve a reference to the RegionLayout object in use
120 {
121 return RLayout;
122 }
124 {
125 return RLayout;
126 }
127
128 // get number of particles on a physical node
129 int getNodeCount(unsigned i) const
130 {
131 PAssert_LT(i, (unsigned int) Ippl::getNodes());
132 return NodeCount[i];
133 }
134
135 // get flag for empty node domain
136 bool getEmptyNode(unsigned i) const
137 {
138 PAssert_LT(i, (unsigned int) Ippl::getNodes());
139 return EmptyNode[i];
140 }
141
142 //
143 // Particle swapping/update routines
144 //
145
146 // Update the location and indices of all atoms in the given IpplParticleBase
147 // object. This handles swapping particles among processors if
148 // needed, and handles create and destroy requests. When complete,
149 // all nodes have correct layout information.
151 const ParticleAttrib<char>* canSwap=0);
152
153
154 //
155 // I/O
156 //
157
158 // Print out information for debugging purposes.
160
161 //
162 // virtual functions for FieldLayoutUser's (and other UserList users)
163 //
164
165 // Repartition onto a new layout
166 virtual void Repartition(UserList *);
167
168 // Tell this object that an object is being deleted
170
171 void enableCaching() { caching = true; }
172 void disableCaching() { caching = false; }
173
174protected:
175 // The RegionLayout which determines where our particles go.
177
178 // The number of particles located on each physical node.
179 size_t *NodeCount;
180
181 // Flag for which nodes have no local domain
183
184 // a list of Message pointers used in swapping particles, and flags
185 // for which nodes expect messages in each dimension
189 std::vector<size_t>* PutList;
190
192
193 // perform common constructor tasks
194 void setup();
195
197 // Rebuild the RegionLayout entirely, by recalculating our min and max
198 // domains, adding a buffer region, and then giving this new Domain to
199 // our internal RegionLayout. When this is done, we must rebuild all
200 // our other data structures as well.
201 template < class PB >
202 void rebuild_layout(size_t haveLocal, PB& PData)
203 {
204 size_t i;
205 unsigned d; // loop variables
206
207 //~ Inform dbgmsg("SpatialLayout::rebuild_layout", INFORM_ALL_NODES);
208 //~ dbgmsg << "rebuild..." << endl;
209 SingleParticlePos_t minpos = 0;
210 SingleParticlePos_t maxpos = 0;
211 int tag = Ippl::Comm->next_tag(P_SPATIAL_RANGE_TAG, P_LAYOUT_CYCLE);
212 int btag = Ippl::Comm->next_tag(P_SPATIAL_RANGE_TAG, P_LAYOUT_CYCLE);
213
214 // if we have local particles, then find the min and max positions
215 if (haveLocal > 0)
216 {
217 minpos = PData.R[0];
218 maxpos = PData.R[0];
219 for (i=1; i < haveLocal; ++i)
220 {
221 for (d=0; d < Dim; ++d)
222 {
223 if (PData.R[i][d] < minpos[d])
224 minpos[d] = PData.R[i][d];
225 if (PData.R[i][d] > maxpos[d])
226 maxpos[d] = PData.R[i][d];
227 }
228 }
229 }
230
231 // if we're not on node 0, send data to node 0
232 if (Ippl::myNode() != 0)
233 {
234 Message *msg = new Message;
235 msg->put(haveLocal);
236 if (haveLocal > 0)
237 {
238 minpos.putMessage(*msg);
239 maxpos.putMessage(*msg);
240 }
241 Ippl::Comm->send(msg, 0, tag);
242
243 // now receive back min and max range as provided by the master node.
244 // These will include some buffer region, and will be integral values,
245 // so we can make a FieldLayout and use it to initialize the RegionLayout.
246 int node = 0;
247 msg = Ippl::Comm->receive_block(node, btag);
248 minpos.getMessage(*msg);
249 maxpos.getMessage(*msg);
250 delete msg;
251
252 }
253 else // on node 0, collect data and compute region
254 {
255 SingleParticlePos_t tmpminpos;
256 SingleParticlePos_t tmpmaxpos;
257 size_t tmphaveLocal = 0;
258 unsigned unreceived = Ippl::getNodes() - 1;
259
260 // collect data from other nodes
261 while (unreceived > 0)
262 {
263 int node = COMM_ANY_NODE;
264 Message *msg = Ippl::Comm->receive_block(node, tag);
265 msg->get(tmphaveLocal);
266 if (tmphaveLocal > 0)
267 {
268 tmpminpos.getMessage(*msg);
269 tmpmaxpos.getMessage(*msg);
270 for (i=0; i < Dim; ++i)
271 {
272 if (tmpminpos[i] < minpos[i])
273 minpos[i] = tmpminpos[i];
274 if (tmpmaxpos[i] > maxpos[i])
275 maxpos[i] = tmpmaxpos[i];
276 }
277 }
278 delete msg;
279 unreceived--;
280 }
281
282 // adjust min and max to include a buffer region and fall on integral
283 // values
284 SingleParticlePos_t extrapos = (maxpos - minpos) * ((T)0.125);
285 maxpos += extrapos;
286 minpos -= extrapos;
287 for (i=0; i < Dim; ++i)
288 {
289 if (minpos[i] >= 0.0)
290 minpos[i] = (int)(minpos[i]);
291 else
292 minpos[i] = (int)(minpos[i] - 1);
293 maxpos[i] = (int)(maxpos[i] + 1);
294 }
295
296 // send these values out to the other nodes
297 if (Ippl::getNodes() > 1)
298 {
299 Message *bmsg = new Message;
300 minpos.putMessage(*bmsg);
301 maxpos.putMessage(*bmsg);
302 Ippl::Comm->broadcast_others(bmsg, btag);
303 }
304 }
305
306 // determine the size of the new domain, and the number of blocks into
307 // which it should be broken
308 NDIndex<Dim> range;
309 for (i=0; i < Dim; ++i)
310 range[i] = Index((int)(minpos[i]), (int)(maxpos[i]));
311 int vn = -1;
312 if (RLayout.initialized())
313 vn = RLayout.size_iv() + RLayout.size_rdv();
314
315 // ask the RegionLayout to change the paritioning to match this size
316 // and block count. This will eventually end up by calling Repartition
317 // here, which will lead to rebuilding the neighbor data, etc., so we
318 // are done.
319 RLayout.changeDomain(range, vn);
320 }
321
322 // swap particles to neighboring nodes if they have moved too far
323 // PB is the type of IpplParticleBase which should have it's layout rebuilt.
324 //mwerks template<class PB>
325 //mwerks unsigned swap_particles(unsigned, PB&);
327 // go through all our local particles, and send particles which must
328 // be swapped to another node to that node.
329 template < class PB >
330 size_t swap_particles(size_t LocalNum, PB& PData)
331 {
332
333//~ Inform dbgmsg("SpatialLayout::swap_particles", INFORM_ALL_NODES);
334 //~ dbgmsg << "swap..." << endl;
335
336 Inform msg("ParticleSpatialLayout ERROR ", INFORM_ALL_NODES);
337
338 unsigned d, i, j; // loop variables
339 size_t ip;
340 unsigned N = Ippl::getNodes();
341 unsigned myN = Ippl::myNode();
342
343 // iterators used to search local domains
344 typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
345
346 // iterators used to search remote domains
347 typename RegionLayout<T,Dim,Mesh>::iterator_dv remoteV; // remoteEnd = RLayout.end_rdv();
348
349 // JCC: This "nudge factor" stuff was added when we were experiencing
350 // problems with particles getting lost in between PRegions on
351 // neighboring nodes. This problem has since been resolved by
352 // fixing the way in which PRegion boundaries are computed, so I am
353 // commenting this out for now. We can bring it back later if the
354 // need arises.
355
356 /*
357
358 // Calculate a 'nudge factor', an amount that can get added to a
359 // particle position to determine where it should be located. The nudge
360 // factor equals 1/100th the smallest width of the rnodes in each dimension.
361 // When we try to find where a particle is located, we check what vnode
362 // contains this particle 'nudge region', a box around the particle's pos
363 // of the size of the nudge factor.
364 T pNudge[Dim];
365 for (d=0; d < Dim; ++d) {
366 // initialize to the first rnode's width
367 T minval = (*(RLayout.begin_iv())).second->getDomain()[d].length();
368
369 // check the local rnodes
370 for (localV = RLayout.begin_iv(); localV != localEnd; ++localV) {
371 T checkval = (*localV).second->getDomain()[d].length();
372 if (checkval < minval)
373 minval = checkval;
374 }
375
376 // check the remote rnodes
377 for (remoteV = RLayout.begin_rdv(); remoteV != remoteEnd; ++remoteV) {
378 T checkval = (*remoteV).second->getDomain()[d].length();
379 if (checkval < minval)
380 minval = checkval;
381 }
382
383 // now rescale the minval, and save it
384 pNudge[d] = 0.00001 * minval;
385 }
386
387 */
388
389 // An NDRegion object used to store a particle position.
390 NDRegion<T,Dim> pLoc;
391
392 // get new message tag for particle exchange with empty domains
393 int etag = Ippl::Comm->next_tag(P_SPATIAL_RETURN_TAG,P_LAYOUT_CYCLE);
394
395 if (!getEmptyNode(myN))
396 {
397
398 // Particles are swapped in multipple passes, one for each dimension.
399 // The tasks completed here for each dimension are the following:
400 // 1. For each local Vnode, find the remote Vnodes which exist along
401 // same axis as the current axis (i.e. all Vnodes along the x-axis).
402 // 2. From this list, determine which nodes we send messages to.
403 // 3. Go through all the particles, finding those which have moved to
404 // an off-processor vnode, and store index in an array for that node
405 // 4. Send off the particles to the nodes (if no particles are
406 // going to a node, send them a message with 0 in it)
407 // 5. Delete the send particles from our local list
408 // 6. Receive particles sent to us by other nodes (some messages may
409 // say that we're receiving 0 particles from that node).
410
411 // Initialize NDRegion with a position inside the first Vnode.
412 // We can skip dim 0, since it will be filled below.
413 for (d = 1; d < Dim; ++d)
414 {
415 T first = (*(RLayout.begin_iv())).second->getDomain()[d].first();
416 T last = (*(RLayout.begin_iv())).second->getDomain()[d].last();
417 T mid = first + 0.5 * (last - first);
418 pLoc[d] = PRegion<T>(mid, mid);
419 }
420
421 for (d = 0; d < Dim; ++d)
422 {
423
424 // get new message tag for particle exchange along this dimension
426
427 // we only need to do the rest if there are other nodes in this dim
428 if (NeighborNodes[d] > 0)
429 {
430 // create new messages to send to our neighbors
431 for (i = 0; i < N; i++)
432 if (SwapNodeList[d][i])
433 SwapMsgList[i] = new Message;
434
435 // Go through the particles and find those moving in the current dir.
436 // When one is found, copy it into outgoing message and delete it.
437 for (ip=0; ip<LocalNum; ++ip)
438 {
439 // get the position of particle ip, and find the closest grid pnt
440 // for just the dimensions 0 ... d
441 for (j = 0; j <= d; j++)
442 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
443
444 // first check local domains (in this dimension)
445 bool foundit = false;
446 // JCC: int nudged = 0;
447 while (!foundit)
448 {
449 for (localV = RLayout.begin_iv();
450 localV != localEnd && !foundit; ++localV)
451 {
452 foundit= (((*localV).second)->getDomain())[d].touches(pLoc[d]);
453 }
454
455 // if not found, it might be remote
456 if (!foundit)
457 {
458 // see which Vnode this postion is in
459 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
460 RLayout.touch_range_rdv(pLoc);
461
462 // make sure we have a vnode to send it to
463 if (touchingVN.first == touchingVN.second)
464 {
465 // JCC: if (nudged >= Dim) {
466 ERRORMSG("Local particle " << ip << " with ID=");
467 ERRORMSG(PData.ID[ip] << " at ");
468 ERRORMSG(PData.R[ip] << " is outside of global domain ");
469 ERRORMSG(RLayout.getDomain() << endl);
470 ERRORMSG("This occurred when searching for point " << pLoc);
471 ERRORMSG(" in RegionLayout = " << RLayout << endl);
472 Ippl::abort();
473 }
474 else
475 {
476
477 // the node has been found - add index to put list
478 unsigned node = (*(touchingVN.first)).second->getNode();
479 PAssert_EQ(SwapNodeList[d][node], true);
480 PutList[node].push_back(ip);
481
482 // .. and then add to DestroyList
483 PData.destroy(1, ip);
484
485 // indicate we found it to quit this check
486 foundit = true;
487 }
488 }
489 }
490 }
491
492 // send the particles to their destination nodes
493 for (i = 0; i < N; i++)
494 {
495 if (SwapNodeList[d][i])
496 {
497 // put data for particles on this put list into message
498 PData.putMessage( *(SwapMsgList[i]), PutList[i] );
499
500 // add a final 'zero' number of particles to indicate the end
501 PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
502
503 // send the message
504 // Inform dbgmsg("SpatialLayout", INFORM_ALL_NODES);
505 //dbgmsg << "Swapping "<<PutList[i].size() << " particles to node ";
506 //dbgmsg << i<<" with tag " << tag << " (" << 'x' + d << ")" << endl;
507 //dbgmsg << " ... msg = " << *(SwapMsgList[i]) << endl;
508 int node = i;
509 Ippl::Comm->send(SwapMsgList[i], node, tag);
510
511 // clear the list
512 PutList[i].erase(PutList[i].begin(), PutList[i].end());
513 }
514 }
515
516 LocalNum -= PData.getDestroyNum(); // update local num
517 ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
518 PData.performDestroy();
519
520 // receive particles from neighbor nodes, and add them to our list
521 unsigned sendnum = NeighborNodes[d];
522 while (sendnum-- > 0)
523 {
525 Message *recmsg = Ippl::Comm->receive_block(node, tag);
526 size_t recvd;
527 while ((recvd = PData.getMessage(*recmsg)) > 0)
528 LocalNum += recvd;
529 delete recmsg;
530 }
531 } // end if (NeighborNodes[d] > 0)
532
533 if (d == 0)
534 {
535 // receive messages from any empty nodes
536 for (i = 0; i < N; ++i)
537 {
538 if (getEmptyNode(i))
539 {
540 int node = i;
541 Message *recmsg = Ippl::Comm->receive_block(node, etag);
542 size_t recvd;
543 while ((recvd = PData.getMessage(*recmsg)) > 0)
544 LocalNum += recvd;
545 delete recmsg;
546 }
547 }
548 }
549
550 } // end for (d=0; d<Dim; ++d)
551
552 }
553 else // empty node sends, but does not receive
554 {
555 msg << "case getEmptyNode(myN) " << endl;
556 // create new messages to send to our neighbors along dim 0
557 for (i = 0; i < N; i++)
558 if (SwapNodeList[0][i])
559 SwapMsgList[i] = new Message;
560
561 // Go through the particles and find those moving to other nodes.
562 // When one is found, copy it into outgoing message and delete it.
563 for (ip=0; ip<LocalNum; ++ip)
564 {
565 // get the position of particle ip, and find the closest grid pnt
566 for (j = 0; j < Dim; j++)
567 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
568
569 // see which remote Vnode this postion is in
570 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
571 RLayout.touch_range_rdv(pLoc);
572
573 // make sure we have a vnode to send it to
574 if (touchingVN.first == touchingVN.second)
575 {
576 ERRORMSG("Local particle " << ip << " with ID=");
577 ERRORMSG(PData.ID[ip] << " at ");
578 ERRORMSG(PData.R[ip] << " is outside of global domain ");
579 ERRORMSG(RLayout.getDomain() << endl);
580 ERRORMSG("This occurred when searching for point " << pLoc);
581 ERRORMSG(" in RegionLayout = " << RLayout << endl);
582 Ippl::abort();
583 }
584 else
585 {
586 // the node has been found - add index to put list
587 unsigned node = (*(touchingVN.first)).second->getNode();
588 PAssert_EQ(SwapNodeList[0][node], true);
589 PutList[node].push_back(ip);
590
591 // .. and then add to DestroyList
592 PData.destroy(1, ip);
593 }
594 }
595
596 // send the particles to their destination nodes
597 for (i = 0; i < N; i++)
598 {
599 if (SwapNodeList[0][i])
600 {
601 // put data for particles on this put list into message
602 PData.putMessage( *(SwapMsgList[i]), PutList[i] );
603
604 // add a final 'zero' number of particles to indicate the end
605 PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
606
607 // send the message
608 int node = i;
609 Ippl::Comm->send(SwapMsgList[i], node, etag);
610
611 // clear the list
612 PutList[i].erase(PutList[i].begin(), PutList[i].end());
613 }
614 }
615
616 LocalNum -= PData.getDestroyNum(); // update local num
617 ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
618 PData.performDestroy();
619
620 }
621
622 // return how many particles we have now
623 return LocalNum;
624 }
625
626
627/*
628 * Simplified version for testing purposes.
629 */
630
631 template < class PB >
632 size_t short_swap_particles(size_t LocalNum, PB& PData)
633 {
634 unsigned d, i, j; // loop variables
635 size_t ip;
636 unsigned N = Ippl::getNodes();
637
638 // iterators used to search local domains
639 typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
640
641 // iterators used to search remote domains
642 typename RegionLayout<T,Dim,Mesh>::iterator_dv remoteV; // remoteEnd = RLayout.end_rdv();
643
644
645 // An NDRegion object used to store a particle position.
646 NDRegion<T,Dim> pLoc;
647
648 // Initialize NDRegion with a position inside the first Vnode.
649 // We can skip dim 0, since it will be filled below.
650 for (d = 1; d < Dim; ++d)
651 {
652 T first = (*(RLayout.begin_iv())).second->getDomain()[d].first();
653 T last = (*(RLayout.begin_iv())).second->getDomain()[d].last();
654 T mid = first + 0.5 * (last - first);
655 pLoc[d] = PRegion<T>(mid, mid);
656 }
657
658 for (d = 0; d < Dim; ++d)
659 {
660
661 // get new message tag for particle exchange along this dimension
663
664 // we only need to do the rest if there are other nodes in this dim
665 if (NeighborNodes[d] > 0)
666 {
667 // create new messages to send to our neighbors
668 for (i = 0; i < N; i++)
669 if (SwapNodeList[d][i])
670 SwapMsgList[i] = new Message;
671
672 // Go through the particles and find those moving in the current dir.
673 // When one is found, copy it into outgoing message and delete it.
674 for (ip=0; ip<LocalNum; ++ip)
675 {
676 // get the position of particle ip, and find the closest grid pnt
677 // for just the dimensions 0 ... d
678 for (j = 0; j <= d; j++)
679 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
680
681 // first check local domains (in this dimension)
682 bool foundit = false;
683
684 for (localV = RLayout.begin_iv();
685 localV != localEnd && !foundit; ++localV)
686 {
687 foundit= (((*localV).second)->getDomain())[d].touches(pLoc[d]);
688 }
689
690 // if not found, it might be remote
691 if (!foundit)
692 {
693 // see which Vnode this postion is in
694 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
695 RLayout.touch_range_rdv(pLoc);
696
697
698 // the node has been found - add index to put list
699 unsigned node = (*(touchingVN.first)).second->getNode();
700 PAssert_EQ(SwapNodeList[d][node], true);
701 PutList[node].push_back(ip);
702
703 // .. and then add to DestroyList
704 PData.destroy(1, ip);
705
706 // indicate we found it to quit this check
707 foundit = true;
708 }
709 }
710
711 std::vector<MPI_Request> requests;
712 std::vector<MsgBuffer*> buffers;
713
714 // send the particles to their destination nodes
715 for (i = 0; i < N; i++)
716 {
717 if (SwapNodeList[d][i])
718 {
719
720 // put data for particles on this put list into message
721 PData.putMessage( *(SwapMsgList[i]), PutList[i] );
722
723 // add a final 'zero' number of particles to indicate the end
724 PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
725
726 int node = i;
727 Ippl::Comm->send(SwapMsgList[i], node, tag);
728
729 // clear the list
730 PutList[i].erase(PutList[i].begin(), PutList[i].end());
731
732
733
734 }
735 }
736
737 LocalNum -= PData.getDestroyNum(); // update local num
738 ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
739 PData.performDestroy();
740
741 // receive particles from neighbor nodes, and add them to our list
742 unsigned sendnum = NeighborNodes[d];
743 while (sendnum-- > 0)
744 {
746 Message *recmsg = Ippl::Comm->receive_block(node, tag);
747 size_t recvd;
748 while ((recvd = PData.getMessage(*recmsg)) > 0)
749 LocalNum += recvd;
750 delete recmsg;
751 }
752
753 } // end if (NeighborNodes[d] > 0)
754
755 } // end for (d=0; d<Dim; ++d)
756
757
758 // return how many particles we have now
759 return LocalNum;
760 }
761
762
763
764
765
766 // PB is the type of IpplParticleBase which should have it's layout rebuilt.
767 //mwerks template<class PB>
768 //mwerks unsigned swap_particles(unsigned, PB&, const ParticleAttrib<char>&);
770 // go through all our local particles, and send particles which must
771 // be swapped to another node to that node.
772 template < class PB >
773 size_t swap_particles(size_t LocalNum, PB& PData,
774 const ParticleAttrib<char>& canSwap)
775 {
776
777 unsigned d, i, j; // loop variables
778 size_t ip;
779 unsigned N = Ippl::getNodes();
780 unsigned myN = Ippl::myNode();
781
782 // iterators used to search local domains
783 typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
784
785 // iterators used to search remote domains
786 typename RegionLayout<T,Dim,Mesh>::iterator_dv remoteV; // remoteEnd = RLayout.end_rdv();
787
788 // JCC: This "nudge factor" stuff was added when we were experiencing
789 // problems with particles getting lost in between PRegions on
790 // neighboring nodes. This problem has since been resolved by
791 // fixing the way in which PRegion boundaries are computed, so I am
792 // commenting this out for now. We can bring it back later if the
793 // need arises.
794
795 /*
796
797 // Calculate a 'nudge factor', an amount that can get added to a
798 // particle position to determine where it should be located. The nudge
799 // factor equals 1/100th the smallest width of the rnodes in each dimension.
800 // When we try to find where a particle is located, we check what vnode
801 // contains this particle 'nudge region', a box around the particle's pos
802 // of the size of the nudge factor.
803 T pNudge[Dim];
804 for (d=0; d < Dim; ++d) {
805 // initialize to the first rnode's width
806 T minval = (*(RLayout.begin_iv())).second->getDomain()[d].length();
807
808 // check the local rnodes
809 for (localV = RLayout.begin_iv(); localV != localEnd; ++localV) {
810 T checkval = (*localV).second->getDomain()[d].length();
811 if (checkval < minval)
812 minval = checkval;
813 }
814
815 // check the remote rnodes
816 for (remoteV = RLayout.begin_rdv(); remoteV != remoteEnd; ++remoteV) {
817 T checkval = (*remoteV).second->getDomain()[d].length();
818 if (checkval < minval)
819 minval = checkval;
820 }
821
822 // now rescale the minval, and save it
823 pNudge[d] = 0.00001 * minval;
824 }
825
826 */
827
828 // An NDRegion object used to store a particle position.
829 NDRegion<T,Dim> pLoc;
830
831 // get new message tag for particle exchange with empty domains
832 int etag = Ippl::Comm->next_tag(P_SPATIAL_RETURN_TAG,P_LAYOUT_CYCLE);
833
834 if (!getEmptyNode(myN))
835 {
836
837 // Particles are swapped in multipple passes, one for each dimension.
838 // The tasks completed here for each dimension are the following:
839 // 1. For each local Vnode, find the remote Vnodes which exist along
840 // same axis as the current axis (i.e. all Vnodes along the x-axis).
841 // 2. From this list, determine which nodes we send messages to.
842 // 3. Go through all the particles, finding those which have moved to
843 // an off-processor vnode, and store index in an array for that node
844 // 4. Send off the particles to the nodes (if no particles are
845 // going to a node, send them a message with 0 in it)
846 // 5. Delete the send particles from our local list
847 // 6. Receive particles sent to us by other nodes (some messages may
848 // say that we're receiving 0 particles from that node).
849
850 // Initialize NDRegion with a position inside the first Vnode.
851 // We can skip dim 0, since it will be filled below.
852 for (d = 1; d < Dim; ++d)
853 {
854 T first = (*(RLayout.begin_iv())).second->getDomain()[d].first();
855 T last = (*(RLayout.begin_iv())).second->getDomain()[d].last();
856 T mid = first + 0.5 * (last - first);
857 pLoc[d] = PRegion<T>(mid, mid);
858 }
859
860 for (d = 0; d < Dim; ++d)
861 {
862
863 // get new message tag for particle exchange along this dimension
865
866 // we only need to do the rest if there are other nodes in this dim
867 if (NeighborNodes[d] > 0)
868 {
869 // create new messages to send to our neighbors
870 for (i = 0; i < N; i++)
871 if (SwapNodeList[d][i])
872 SwapMsgList[i] = new Message;
873
874 // Go through the particles and find those moving in the current dir.
875 // When one is found, copy it into outgoing message and delete it.
876 for (ip=0; ip<LocalNum; ++ip)
877 {
878 if (!bool(canSwap[ip])) continue; // skip if can't swap
879 // get the position of particle ip, and find the closest grid pnt
880 // for just the dimensions 0 ... d
881 for (j = 0; j <= d; j++)
882 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
883
884 // first check local domains (in this dimension)
885 bool foundit = false;
886 // JCC: int nudged = 0;
887 while (!foundit)
888 {
889 for (localV = RLayout.begin_iv();
890 localV != localEnd && !foundit; ++localV)
891 {
892 foundit= (((*localV).second)->getDomain())[d].touches(pLoc[d]);
893 }
894
895 // if not found, it might be remote
896 if (!foundit)
897 {
898 // see which Vnode this postion is in
899 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
900 RLayout.touch_range_rdv(pLoc);
901
902 // make sure we have a vnode to send it to
903 if (touchingVN.first == touchingVN.second)
904 {
905 // JCC: if (nudged >= Dim) {
906 ERRORMSG("Local particle " << ip << " with ID=");
907 ERRORMSG(PData.ID[ip] << " at ");
908 ERRORMSG(PData.R[ip] << " is outside of global domain ");
909 ERRORMSG(RLayout.getDomain() << endl);
910 ERRORMSG("This occurred when searching for point " << pLoc);
911 ERRORMSG(" in RegionLayout = " << RLayout << endl);
912 Ippl::abort();
913 }
914 else
915 {
916 // the node has been found - add index to put list
917 unsigned node = (*(touchingVN.first)).second->getNode();
918 PAssert_EQ(SwapNodeList[d][node], true);
919 PutList[node].push_back(ip);
920
921 // .. and then add to DestroyList
922 PData.destroy(1, ip);
923
924 // indicate we found it to quit this check
925 foundit = true;
926 }
927 }
928 }
929 }
930
931 // send the particles to their destination nodes
932 for (i = 0; i < N; i++)
933 {
934 if (SwapNodeList[d][i])
935 {
936 // put data for particles on this put list into message
937 PData.putMessage( *(SwapMsgList[i]), PutList[i] );
938
939 // add a final 'zero' number of particles to indicate the end
940 PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
941
942 // send the message
943 //Inform dbgmsg("SpatialLayout", INFORM_ALL_NODES);
944 //dbgmsg << "Swapping "<<PutList[i].size() << " particles to node ";
945 //dbgmsg << i<<" with tag " << tag << " (" << 'x' + d << ")" << endl;
946 //dbgmsg << " ... msg = " << *(SwapMsgList[i]) << endl;
947 int node = i;
948 Ippl::Comm->send(SwapMsgList[i], node, tag);
949
950 // clear the list
951 PutList[i].erase(PutList[i].begin(), PutList[i].end());
952 }
953 }
954
955 LocalNum -= PData.getDestroyNum(); // update local num
956 ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
957 PData.performDestroy();
958
959 // receive particles from neighbor nodes, and add them to our list
960 unsigned sendnum = NeighborNodes[d];
961 while (sendnum-- > 0)
962 {
964 Message *recmsg = Ippl::Comm->receive_block(node, tag);
965 size_t recvd;
966 while ((recvd = PData.getMessage(*recmsg)) > 0)
967 LocalNum += recvd;
968 delete recmsg;
969 }
970 } // end if (NeighborNodes[d] > 0)
971
972 if (d == 0)
973 {
974 // receive messages from any empty nodes
975 for (i = 0; i < N; ++i)
976 {
977 if (getEmptyNode(i))
978 {
979 int node = i;
980 Message *recmsg = Ippl::Comm->receive_block(node, etag);
981 size_t recvd;
982 while ((recvd = PData.getMessage(*recmsg)) > 0)
983 LocalNum += recvd;
984 delete recmsg;
985 }
986 }
987 }
988
989 } // end for (d=0; d<Dim; ++d)
990
991 }
992 else // empty node sends, but does not receive
993 {
994 // create new messages to send to our neighbors along dim 0
995 for (i = 0; i < N; i++)
996 if (SwapNodeList[0][i])
997 SwapMsgList[i] = new Message;
998
999 // Go through the particles and find those moving to other nodes.
1000 // When one is found, copy it into outgoing message and delete it.
1001 for (ip=0; ip<LocalNum; ++ip)
1002 {
1003 if (!bool(canSwap[ip])) continue; // skip if can't swap
1004 // get the position of particle ip, and find the closest grid pnt
1005 for (j = 0; j < Dim; j++)
1006 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
1007
1008 // see which remote Vnode this postion is in
1009 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
1010 RLayout.touch_range_rdv(pLoc);
1011
1012 // make sure we have a vnode to send it to
1013 if (touchingVN.first == touchingVN.second)
1014 {
1015 ERRORMSG("Local particle " << ip << " with ID=");
1016 ERRORMSG(PData.ID[ip] << " at ");
1017 ERRORMSG(PData.R[ip] << " is outside of global domain ");
1018 ERRORMSG(RLayout.getDomain() << endl);
1019 ERRORMSG("This occurred when searching for point " << pLoc);
1020 ERRORMSG(" in RegionLayout = " << RLayout << endl);
1021 Ippl::abort();
1022 }
1023 else
1024 {
1025 // the node has been found - add index to put list
1026 unsigned node = (*(touchingVN.first)).second->getNode();
1027 PAssert_EQ(SwapNodeList[0][node], true);
1028 PutList[node].push_back(ip);
1029
1030 // .. and then add to DestroyList
1031 PData.destroy(1, ip);
1032 }
1033 }
1034
1035 // send the particles to their destination nodes
1036 for (i = 0; i < N; i++)
1037 {
1038 if (SwapNodeList[0][i])
1039 {
1040 // put data for particles on this put list into message
1041 PData.putMessage( *(SwapMsgList[i]), PutList[i] );
1042
1043 // add a final 'zero' number of particles to indicate the end
1044 PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
1045
1046 // send the message
1047 int node = i;
1048 Ippl::Comm->send(SwapMsgList[i], node, etag);
1049
1050 // clear the list
1051 PutList[i].erase(PutList[i].begin(), PutList[i].end());
1052 }
1053 }
1054
1055 LocalNum -= PData.getDestroyNum(); // update local num
1056 ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
1057 PData.performDestroy();
1058
1059 }
1060
1061 // return how many particles we have now
1062 return LocalNum;
1063 }
1064
1065
1066
1067
1068/*
1069 * Newer (cleaner) version of swap particles that uses less bandwidth
1070 * and drastically lowers message counts for real cases.
1071 */
1072 template < class PB >
1073 size_t new_swap_particles(size_t LocalNum, PB& PData)
1074 {
1075 Ippl::Comm->barrier();
1076
1077 unsigned N = Ippl::getNodes();
1078 unsigned myN = Ippl::myNode();
1079
1080 typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
1082
1083 std::vector<int> msgsend(N, 0);
1084 std::vector<int> msgrecv(N, 0);
1085
1086 NDRegion<T,Dim> pLoc;
1087
1088 std::multimap<unsigned, unsigned> p2n; //<node ID, particle ID>
1089
1090 bool responsibleNodeNotFound = false;
1091 for (unsigned int ip=0; ip<LocalNum; ++ip)
1092 {
1093 for (unsigned int j = 0; j < Dim; j++)
1094 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
1095
1096 unsigned destination = myN;
1097 bool found = false;
1098 for (localV = RLayout.begin_iv(); localV != localEnd && !found; ++localV)
1099 {
1100 if ((((*localV).second)->getDomain()).touches(pLoc))
1101 found = true; // particle is local and doesn't need to be sent anywhere
1102 }
1103
1104 if (found)
1105 continue;
1106
1107 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN = RLayout.touch_range_rdv(pLoc);
1108
1109 //external location
1110 if (touchingVN.first == touchingVN.second) {
1111 responsibleNodeNotFound = true;
1112 break;
1113 }
1114 destination = (*(touchingVN.first)).second->getNode();
1115
1116 msgsend[destination] = 1;
1117
1118 p2n.insert(std::pair<unsigned, unsigned>(destination, ip));
1119 }
1120
1121 allreduce(&responsibleNodeNotFound,
1122 1,
1123 std::logical_or<bool>());
1124
1125 if (responsibleNodeNotFound) {
1126 throw IpplException("ParticleSpatialLayout::new_swap_particles",
1127 "could not find node responsible for particle");
1128 }
1129
1130 //reduce message count so every node knows how many messages to receive
1131 allreduce(msgsend.data(), msgrecv.data(), N, std::plus<int>());
1132
1134
1135 typename std::multimap<unsigned, unsigned>::iterator i = p2n.begin();
1136
1137 std::unique_ptr<Format> format(PData.getFormat());
1138
1139
1140 std::vector<MPI_Request> requests;
1141 std::vector<std::shared_ptr<MsgBuffer> > buffers;
1142
1143 while (i!=p2n.end())
1144 {
1145 unsigned cur_destination = i->first;
1146
1147 std::shared_ptr<MsgBuffer> msgbuf(new MsgBuffer(format.get(), p2n.count(i->first)));
1148
1149 for (; i!=p2n.end() && i->first == cur_destination; ++i)
1150 {
1151 Message msg;
1152 PData.putMessage(msg, i->second);
1153 PData.destroy(1, i->second);
1154 msgbuf->add(&msg);
1155 }
1156
1157 MPI_Request request = Ippl::Comm->raw_isend( msgbuf->getBuffer(), msgbuf->getSize(), cur_destination, tag);
1158
1159 //remember request and buffer so we can delete them later
1160 requests.push_back(request);
1161 buffers.push_back(msgbuf);
1162 }
1163
1164 LocalNum -= PData.getDestroyNum(); // update local num
1165 PData.performDestroy();
1166
1167 //receive new particles
1168 for (int k = 0; k<msgrecv[myN]; ++k)
1169 {
1170 int node = Communicate::COMM_ANY_NODE;
1171 char *buffer = 0;
1172 int bufsize = Ippl::Comm->raw_probe_receive(buffer, node, tag);
1173 MsgBuffer recvbuf(format.get(), buffer, bufsize);
1174
1175 Message *msg = recvbuf.get();
1176 while (msg != 0)
1177 {
1178 LocalNum += PData.getSingleMessage(*msg);
1179 delete msg;
1180 msg = recvbuf.get();
1181 }
1182
1183
1184 }
1185
1186 //wait for communication to finish and clean up buffers
1187 MPI_Request* requests_ptr = requests.empty()? static_cast<MPI_Request*>(0): &(requests[0]);
1188 MPI_Waitall(requests.size(), requests_ptr, MPI_STATUSES_IGNORE);
1189
1190 return LocalNum;
1191 }
1192
1193 template < class PB >
1194 size_t new_swap_particles(size_t LocalNum, PB& PData,
1195 const ParticleAttrib<char>& canSwap)
1196 {
1197 Ippl::Comm->barrier();
1198
1199 unsigned N = Ippl::getNodes();
1200 unsigned myN = Ippl::myNode();
1201
1202 typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
1204
1205 std::vector<int> msgsend(N, 0);
1206 std::vector<int> msgrecv(N, 0);
1207
1208 NDRegion<T,Dim> pLoc;
1209
1210 std::multimap<unsigned, unsigned> p2n; //<node ID, particle ID>
1211
1212 bool responsibleNodeNotFound = false;
1213 for (unsigned int ip=0; ip<LocalNum; ++ip)
1214 {
1215 if (!bool(canSwap[ip]))//skip if it can't be swapped
1216 continue;
1217
1218 for (unsigned int j = 0; j < Dim; j++)
1219 pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
1220
1221 unsigned destination = myN;
1222 bool found = false;
1223 for (localV = RLayout.begin_iv(); localV != localEnd && !found; ++localV)
1224 {
1225 if ((((*localV).second)->getDomain()).touches(pLoc))
1226 found = true; // particle is local and doesn't need to be sent anywhere
1227 }
1228
1229 if (found)
1230 continue;
1231
1232 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN = RLayout.touch_range_rdv(pLoc);
1233
1234 //external location
1235 if (touchingVN.first == touchingVN.second) {
1236 responsibleNodeNotFound = true;
1237 break;
1238 }
1239 destination = (*(touchingVN.first)).second->getNode();
1240
1241 msgsend[destination] = 1;
1242
1243 p2n.insert(std::pair<unsigned, unsigned>(destination, ip));
1244 }
1245
1246 allreduce(&responsibleNodeNotFound,
1247 1,
1248 std::logical_or<bool>());
1249
1250 if (responsibleNodeNotFound) {
1251 throw IpplException("ParticleSpatialLayout::new_swap_particles",
1252 "could not find node responsible for particle");
1253 }
1254
1255 //reduce message count so every node knows how many messages to receive
1256 allreduce(msgsend.data(), msgrecv.data(), N, std::plus<int>());
1257
1259
1260 typename std::multimap<unsigned, unsigned>::iterator i = p2n.begin();
1261
1262 std::unique_ptr<Format> format(PData.getFormat());
1263
1264 std::vector<MPI_Request> requests;
1265 std::vector<std::shared_ptr<MsgBuffer> > buffers;
1266
1267 while (i!=p2n.end())
1268 {
1269 unsigned cur_destination = i->first;
1270
1271 std::shared_ptr<MsgBuffer> msgbuf(new MsgBuffer(format.get(), p2n.count(i->first)));
1272
1273 for (; i!=p2n.end() && i->first == cur_destination; ++i)
1274 {
1275 Message msg;
1276 PData.putMessage(msg, i->second);
1277 PData.destroy(1, i->second);
1278 msgbuf->add(&msg);
1279 }
1280
1281 MPI_Request request = Ippl::Comm->raw_isend( msgbuf->getBuffer(), msgbuf->getSize(), cur_destination, tag);
1282
1283 //remember request and buffer so we can delete them later
1284 requests.push_back(request);
1285 buffers.push_back(msgbuf);
1286 }
1287
1288 LocalNum -= PData.getDestroyNum(); // update local num
1289 PData.performDestroy();
1290
1291 //receive new particles
1292 for (int k = 0; k<msgrecv[myN]; ++k)
1293 {
1294 int node = Communicate::COMM_ANY_NODE;
1295 char *buffer = 0;
1296 int bufsize = Ippl::Comm->raw_probe_receive(buffer, node, tag);
1297 MsgBuffer recvbuf(format.get(), buffer, bufsize);
1298
1299 Message *msg = recvbuf.get();
1300 while (msg != 0)
1301 {
1302 LocalNum += PData.getSingleMessage(*msg);
1303 delete msg;
1304 msg = recvbuf.get();
1305 }
1306 }
1307
1308 //wait for communication to finish and clean up buffers
1309 MPI_Request* requests_ptr = requests.empty()? static_cast<MPI_Request*>(0): &(requests[0]);
1310 MPI_Waitall(requests.size(), requests_ptr, 0);
1311
1312 return LocalNum;
1313 }
1314
1315};
1316
1318
1319#endif // PARTICLE_SPATIAL_LAYOUT_H
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
const unsigned Dim
void allreduce(const T *input, T *output, int count, Op op)
const int COMM_ANY_NODE
Definition Communicate.h:40
#define P_SPATIAL_TRANSFER_TAG
Definition Tags.h:82
#define P_SPATIAL_RANGE_TAG
Definition Tags.h:84
#define P_LAYOUT_CYCLE
Definition Tags.h:86
#define P_SPATIAL_RETURN_TAG
Definition Tags.h:81
std::ostream & operator<<(std::ostream &, const ParticleSpatialLayout< T, Dim, Mesh, CachingPolicy > &)
Inform & endl(Inform &inf)
Definition Inform.cpp:42
#define INFORM_ALL_NODES
Definition Inform.h:39
#define PAssert_LT(a, b)
Definition PAssert.h:106
#define PAssert_EQ(a, b)
Definition PAssert.h:104
#define ADDIPPLSTAT(stat, amount)
Definition IpplStats.h:237
#define ERRORMSG(msg)
Definition IpplInfo.h:350
Message & getMessage(Message &m)
Message & putMessage(Message &m) const
Definition Index.h:237
Message & put(const T &val)
Definition Message.h:406
Message & get(const T &cval)
Definition Message.h:476
Message * get()
Definition MsgBuffer.cpp:71
int getNodeCount(unsigned i) const
void rebuild_layout(size_t haveLocal, PB &PData)
ParticleAttrib< Index_t > ParticleIndex_t
ParticleAttrib< SingleParticlePos_t > ParticlePos_t
const RegionLayout< T, Dim, Mesh > & getLayout() const
bool getEmptyNode(unsigned i) const
virtual void Repartition(UserList *)
RegionLayout< T, Dim, Mesh > & getLayout()
virtual void notifyUserOfDelete(UserList *)
ParticleLayout< double, Dim >::SingleParticlePos_t SingleParticlePos_t
RegionLayout< double, Dim, UniformCartesian< Dim, double > > RLayout
void update(IpplParticleBase< ParticleSpatialLayout< T, Dim, Mesh, CachingPolicy > > &p, const ParticleAttrib< char > *canSwap=0)
size_t new_swap_particles(size_t LocalNum, PB &PData, const ParticleAttrib< char > &canSwap)
ParticleLayout< double, Dim >::Index_t Index_t
size_t new_swap_particles(size_t LocalNum, PB &PData)
std::vector< size_t > * PutList
size_t swap_particles(size_t LocalNum, PB &PData)
ParticleSpatialLayout(FieldLayout< Dim > &, Mesh &)
size_t short_swap_particles(size_t LocalNum, PB &PData)
FieldLayout< Dim > & getFieldLayout()
unsigned NeighborNodes[Dim]
RegionLayout< double, Dim, UniformCartesian< Dim, double > > RegionLayout_t
ParticleSpatialLayout(const RegionLayout< T, Dim, Mesh > &)
ParticleSpatialLayout(FieldLayout< Dim > &)
size_t swap_particles(size_t LocalNum, PB &PData, const ParticleAttrib< char > &canSwap)
Vektor< T, Dim > SingleParticlePos_t
ac_domain_vnodes::iterator iterator_dv
ac_id_vnodes::iterator iterator_iv
std::pair< touch_iterator_dv, touch_iterator_dv > touch_range_dv
static void abort(const char *=0)
Definition IpplInfo.cpp:616
static int getNodes()
Definition IpplInfo.cpp:670
static int myNode()
Definition IpplInfo.cpp:691
static Communicate * Comm
Definition IpplInfo.h:84