OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
OPAL
ParticleCashedLayout.hpp
Go to the documentation of this file.
1 //
2 // Class ParticleCashedLayout
3 // Please note: for the time being this class is *not* used! But since it
4 // might be used in future projects, we keep this file.
5 //
6 // Copyright (c) 2003 - 2020, Paul Scherrer Institut, Villigen PSI, Switzerland
7 // All rights reserved
8 //
9 // This file is part of OPAL.
10 //
11 // OPAL is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with OPAL. If not, see <https://www.gnu.org/licenses/>.
18 //
19 
23 #include "Region/RegionLayout.h"
25 #include "Utility/IpplInfo.h"
26 #include "Message/Communicate.h"
27 #include "Message/Message.h"
28 
29 #include <algorithm>
30 
31 /* Here we check for every particle if its in one of the cashed region
32  * of a neighbour and then add it as a ghost particle to this neighbor.
33  *
34  * o we dont need a pairlist, but another particle container
35  * maybe we can forget about the whole pairlist thingy and just
36  * USE the ghost particles! so we actually only have to make sure the
37  * ghost particles are uptodate and correct...
38  * o cashed particles are stored int ghost holder of particles!
39  *
40  * FIXME:
41  * o CHECK RADIUS, UNITS! and stuff
42  * checkdist = r - d/2 + eps
43  * o WE MUST ALLOW NO LOCAL GHOST NODES!
44  */
45 
47 // constructor, from a FieldLayout
48 template < class T, unsigned Dim, class Mesh >
50  fl)
52  setup(); // perform necessary setup
53 }
54 
55 
57 // constructor, from a FieldLayout
58 template < class T, unsigned Dim, class Mesh >
60  fl, Mesh& mesh)
61  : ParticleSpatialLayout<T,Dim,Mesh>(fl,mesh) {
62  setup(); // perform necessary setup
63 }
64 
65 
67 // constructor, from a RegionLayout
68 template < class T, unsigned Dim, class Mesh >
71  setup(); // perform necessary setup
72 }
73 
74 
76 // default constructor ... this does not initialize the RegionLayout,
77 // it will be instead initialized during the first update.
78 template < class T, unsigned Dim, class Mesh >
81  setup(); // perform necessary setup
82 }
83 
84 
86 // perform common constructor tasks
87 template < class T, unsigned Dim, class Mesh >
89 
90  // create storage for message pointers used in swapping particles
91  unsigned N = Ippl::getNodes();
92  InterNodeList = new bool[N];
93  SentToNodeList = new bool[N];
94  InteractionNodes = 0;
95 
96  // initialize interaction radius information
97  InterRadius = 0;
98  MaxGlobalInterRadius = 0;
99 }
100 
101 
103 // destructor
104 template < class T, unsigned Dim, class Mesh >
106 
107  delete [] InterNodeList;
108  delete [] SentToNodeList;
109 
110 }
111 
112 
114 // Return the maximum interaction radius of the local particles.
115 template < class T, unsigned Dim, class Mesh >
117 
118  return InterRadius;
119 }
120 
121 
123 // Filling ghost particle data
124 // If this is the first call of this
125 // method after update(), this must make sure up-to-date info on particles
126 // from neighboring nodes is available.
127 template < class T, unsigned Dim, class Mesh >
129 
130  //IFF: all the swap_ghost_particle methods will return immediately if
131  //no swap is necessary
132 
133  // check if we have any particle boundary conditions
134  if (getUpdateFlag(ParticleLayout<T,Dim>::BCONDS)) {
135  // check which boundaries, if any, are periodic
136  ParticleBConds<T,Dim>& pBConds = this->getBConds();
137  bool periodicBC[2*Dim];
138  unsigned numPeriodicBC = 0;
140  for (unsigned d=0; d<2*Dim; ++d) {
141  periodicBC[d] = (pBConds[d] == periodicBCond);
142  if (periodicBC[d]) ++numPeriodicBC;
143  }
144  if (numPeriodicBC>0) {
145  // we need to reflect domains across all periodic boundaries
146  // call specialized function to update ghost particle data
147  swap_ghost_particles(PData.getLocalNum(), PData, periodicBC);
148  }
149  else { // no periodic boundaries, call standard function
150  swap_ghost_particles(PData.getLocalNum(), PData);
151  }
152  }
153  else { // no boundary conditions, call standard function
154  // update ghost particle data if necessary
155  swap_ghost_particles(PData.getLocalNum(), PData);
156  }
157 
158  //IFF: PData now holds ghost particles
159 
160  return;
161 }
162 
163 
165 // for each dimension, calculate where neighboring Vnodes and physical
166 // nodes are located, and which nodes are within interaction distance
167 // of our own Vnodes. Save this info for use in sending ghost particles.
168 template < class T, unsigned Dim, class Mesh >
170 
171  unsigned int j, d; // loop variables
172 
173  // initialize data about interaction nodes, and get the inter radius
174  InteractionNodes = 0;
175  //FIXME: SAME FIX AS BELOW
176  T interRad = getMaxInteractionRadius();
177 
178  // initialize the message list and initial node count
179  unsigned N = Ippl::getNodes();
180  for (j=0; j < N; ++j)
181  InterNodeList[j] = false;
182 
183  // if no interaction radius, we're done
184  if (interRad <= 0.0)
185  return;
186 
187  // get RegionLayout iterators
188  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
189  // determine which physical nodes are in our interaction domain
190  for (localVN = this->RLayout.begin_iv(); localVN != endLocalVN; ++localVN) {
191  // for each local Vnode, the domain to check equals the local Vnode dom
192  // plus twice the interaction radius
193  NDRegion<T,Dim> chkDom((*localVN).second->getDomain());
194  for (d=0; d < Dim; ++d) {
195  chkDom[d] = PRegion<T>(chkDom[d].first() - interRad,
196  chkDom[d].last() + interRad);
197  }
198 
199  // use the RegionLayout to find all remote Vnodes which touch the domain
200  // being checked here
201  typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
202  this->RLayout.touch_range_rdv(chkDom);
203  typename RegionLayout<T,Dim,Mesh>::touch_iterator_dv tVN = touchingVN.first;
204  for ( ; tVN != touchingVN.second; ++tVN) {
205  // note that we need to send a message to the node which contains
206  // this remote Vnode
207  unsigned vn = ((*tVN).second)->getNode();
208  if ( ! InterNodeList[vn] ) {
209  InterNodeList[vn] = true;
210  InteractionNodes++;
211  }
212  }
213  }
214 
215  // set the flag indicating the swap ghost particle routine should
216  // be called the next time we try to access the cashed particles
217  // or do anything of utility with the ghost particles
218  NeedGhostSwap = true;
219  return;
220 }
221 
222 
224 // for each dimension, calculate where neighboring Vnodes and physical
225 // nodes are located, and which nodes are within interaction distance
226 // of our own Vnodes. Save this info for use in sending ghost particles.
227 // Special version to handle periodic boundary conditions
228 template < class T, unsigned Dim, class Mesh >
230  const bool periodicBC[2*Dim])
231 {
232  unsigned j, d; // loop variables
233  unsigned pe = Ippl::myNode();
234 
235  // initialize data about interaction nodes, and get the inter radius
236  InteractionNodes = 0;
237  //FIXME: SAME FIX AS BELOW
238  T interRad = getMaxInteractionRadius();
239 
240  // initialize the message list and initial node count
241  unsigned N = Ippl::getNodes();
242  for (j=0; j < N; ++j)
243  InterNodeList[j] = false;
244 
245  // if no interaction radius, we're done
246  if (interRad <= 0.0)
247  return;
248 
249  // get domain info
250  const NDRegion<T,Dim>& globalDom = this->RLayout.getDomain();
251 
252  // some stuff for computing reflected domains
253  T offset[Dim];
254  unsigned numRef;
255  bool flipBit, activeBit[Dim], refBit[Dim];
256  NDRegion<T,Dim> chkDom, refDom;
257 
258  // get RegionLayout iterators
259  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
260  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN2;
261  typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN;
263 
264  // determine which physical nodes are in our interaction domain
265  for (localVN = this->RLayout.begin_iv(); localVN != endLocalVN; ++localVN) {
266  // for each local Vnode, the domain to check equals the local Vnode dom
267  // plus twice the interaction radius
268  chkDom = (*localVN).second->getDomain();
269  for (d=0; d<Dim; ++d) {
270  chkDom[d] = PRegion<T>(chkDom[d].first() - interRad,
271  chkDom[d].last() + interRad);
272  }
273 
274  // use the RegionLayout to find all remote Vnodes which touch
275  // the domain being checked here
276  touchingVN = this->RLayout.touch_range_rdv(chkDom);
277  for (tVN = touchingVN.first; tVN != touchingVN.second; ++tVN) {
278  // note that we need to send a message to the node which contains
279  // this remote Vnode
280  unsigned vn = ((*tVN).second)->getNode();
281  if ( ! InterNodeList[vn] ) {
282  InterNodeList[vn] = true;
283  InteractionNodes++;
284  }
285  }
286 
287  // look for boundary crossings and check reflected domains
288  numRef = 0;
289  for (d=0; d<Dim; ++d) {
290  if (periodicBC[2*d] && chkDom[d].first()<globalDom[d].first()) {
291  // crossed lower boundary
292  offset[d] = globalDom[d].length();
293  activeBit[d] = true;
294  numRef = 2 * numRef + 1;
295  }
296  else if (periodicBC[2*d+1] && chkDom[d].last()>globalDom[d].last()) {
297  // crossed upper boundary
298  offset[d] = -globalDom[d].length();
299  activeBit[d] = true;
300  numRef = 2 * numRef + 1;
301  }
302  else {
303  offset[d] = 0.0;
304  activeBit[d] = false;
305  }
306  refBit[d] = false; // reset reflected domain bools
307  }
308 
309  // compute and check each domain reflection
310  for (j=0; j<numRef; ++j) {
311  // set up reflected domain: first initialize to original domain
312  refDom = chkDom;
313  // find next combination of dimension offsets
314  d = 0;
315  flipBit = false;
316  while (d<Dim && !flipBit) {
317  // first check if this dim is active
318  if (activeBit[d]) {
319  // now flip bit for this dimension
320  if (refBit[d]) {
321  // flip this bit off and proceed to next dim
322  refBit[d] = false;
323  }
324  else { // refBit[d] is off
325  // flip this bit on and indicate we're done
326  refBit[d] = true;
327  flipBit = true;
328  }
329  }
330  ++d;
331  }
332  PAssert(flipBit); // check that we found next combination
333 
334  // now offset the reflected domain
335  for (d=0; d<Dim; ++d) {
336  if (refBit[d]) refDom[d] = refDom[d] + offset[d];
337  }
338 
339  // use the RegionLayout to find all remote Vnodes which touch
340  // the domain being checked here
341  touchingVN = this->RLayout.touch_range_rdv(refDom);
342  for (tVN = touchingVN.first; tVN != touchingVN.second; ++tVN) {
343  // note that we need to send a message to the node which contains
344  // this remote Vnode
345  unsigned vn = ((*tVN).second)->getNode();
346  if ( ! InterNodeList[vn] ) {
347  InterNodeList[vn] = true;
348  InteractionNodes++;
349  }
350  }
351 
352  if (!InterNodeList[pe]) { // check if we interact with our own domains
353  // for reflected domains, we also must check against local domains
354  bool interact = false;
355  localVN2 = this->RLayout.begin_iv();
356  while (localVN2 != endLocalVN && !interact) {
357  interact = refDom.touches((*localVN2).second->getDomain());
358  ++localVN2;
359  }
360  if (interact) {
361  InterNodeList[pe] = true;
362  InteractionNodes++;
363  }
364  }
365  }
366 
367  }
368 
369  // set the flag indicating the swap ghost particle routine should
370  // be called the next time we try to access the cashed particles
371  // or do anything of utility with the ghost particles
372  NeedGhostSwap = true;
373  return;
374 }
375 
376 
378 // Update the location and indices of all atoms in the given IpplParticleBase
379 // object. This handles swapping particles among processors if
380 // needed, and handles create and destroy requests. When complete,
381 // all nodes have correct layout information.
382 template < class T, unsigned Dim, class Mesh >
385  const ParticleAttrib<char>* canSwap) {
386 
387  unsigned N = Ippl::getNodes();
388  unsigned myN = Ippl::myNode();
389  unsigned LocalNum = PData.getLocalNum();
390  unsigned DestroyNum = PData.getDestroyNum();
391  unsigned TotalNum;
392  //FIXME:
393  //T maxrad = getMaxLocalInteractionRadius();
394  T maxrad = 32.0;
395  int node;
396 
397  // delete particles in destroy list, update local num
398  PData.performDestroy();
399  LocalNum -= DestroyNum;
400 
401  // set up our layout, if not already done ... we could also do this if
402  // we needed to expand our spatial region.
403  if ( ! this->RLayout.initialized())
404  this->rebuild_layout(LocalNum,PData);
405 
406  // apply boundary conditions to the particle positions
407  if (this->getUpdateFlag(ParticleLayout<T,Dim>::BCONDS))
408  this->apply_bconds(LocalNum, PData.R, this->getBConds(), this->RLayout.getDomain());
409 
410  // Now we can swap particles that have moved outside the region of
411  // local field space. This is done in several passes, one for each
412  // spatial dimension. The NodeCount values are updated by this routine.
413  if (N > 1 && this->getUpdateFlag(this->SWAP)) {
414  if (canSwap==0)
415  LocalNum = this->swap_particles(LocalNum, PData);
416  else
417  LocalNum = this->swap_particles(LocalNum, PData, *canSwap);
418  }
419 
420  // flag we need to update our ghost particles
421  NeedGhostSwap = true;
422 
423  // Save how many local particles we have.
424  TotalNum = this->NodeCount[myN] = LocalNum;
425 
426  // there is extra work to do if there are multipple nodes, to distribute
427  // the particle layout data to all nodes
428  if (N > 1) {
429  // At this point, we can send our particle count updates to node 0, and
430  // receive back the particle layout.
433  if (myN != 0) {
434  Message *msg = new Message;
435 
436  // put local particle count in the message
437  msg->put(LocalNum);
438 
439  // also put in our maximum interaction radius
440  msg->put(maxrad);
441 
442  // send this info to node 0
443  Ippl::Comm->send(msg, 0, tag1);
444 
445  // receive back the number of particles on each node, and the maximum
446  // interaction radius
447  node = 0;
448  msg = Ippl::Comm->receive_block(node, tag2);
449  msg->get(this->NodeCount);
450  msg->get(maxrad);
451  msg->get(TotalNum);
452  } else { // do update tasks particular to node 0
453  // receive messages from other nodes describing what they have
454  int notrecvd = N - 1; // do not need to receive from node 0
455  TotalNum = LocalNum;
456  while (notrecvd > 0) {
457  // receive a message from another node. After recv, node == sender.
459  Message *msg = Ippl::Comm->receive_block(node, tag1);
460  int remNodeCount = 0;
461  T remMaxRad = 0.0;
462  msg->get(remNodeCount);
463  msg->get(remMaxRad);
464  delete msg;
465  notrecvd--;
466 
467  // update values based on data from remote node
468  TotalNum += remNodeCount;
469  this->NodeCount[node] = remNodeCount;
470  if (remMaxRad > maxrad)
471  maxrad = remMaxRad;
472  }
473 
474  // send info back to all the client nodes
475  Message *msg = new Message;
476  msg->put(this->NodeCount, this->NodeCount + N);
477  msg->put(maxrad);
478  msg->put(TotalNum);
479  Ippl::Comm->broadcast_others(msg, tag2);
480  }
481  }
482 
483  // update our particle number counts
484  PData.setTotalNum(TotalNum); // set the total atom count
485  PData.setLocalNum(LocalNum); // set the number of local atoms
486 
487  // if the interaction radius changed, must recalculate some things
488  if (maxrad != getMaxInteractionRadius()) {
489  setMaxInteractionRadius(maxrad);
490  // check if we have any particle boundary conditions
491  if (this->getUpdateFlag(ParticleLayout<T,Dim>::BCONDS)) {
492  // check which boundaries, if any, are periodic
493  ParticleBConds<T,Dim>& pBConds = this->getBConds();
494  bool periodicBC[2*Dim];
495  unsigned numPeriodicBC = 0;
497  for (unsigned d=0; d<2*Dim; ++d) {
498  periodicBC[d] = (pBConds[d] == periodicBCond);
499  if (periodicBC[d]) ++numPeriodicBC;
500  }
501  if (numPeriodicBC>0) {
502  // we need to reflect domains across all periodic boundaries
503  // call specialized function
504  rebuild_interaction_data(periodicBC);
505  }
506  else { // no periodic boundaries, call standard function
507  rebuild_interaction_data();
508  }
509  }
510  else { // no boundary conditions, call standard function
511  rebuild_interaction_data();
512  }
513  }
514  return;
515 }
516 
517 
519 // copy particles to other nodes for pairlist computation. The arguments
520 // are the current number of local particles, and the IpplParticleBase object.
521 // Make sure not to send any particles to, or receive particles from,
522 // nodes which have no particles on them. This also takes care of
523 // building the pairlists.
524 //FIXME: is this not working anyways?
525 template < class T, unsigned Dim, class Mesh >
527  LocalNum,
529 
530  unsigned int i; // loop variables
531 
532  // if we've already swapped particles since the last update, we're done
533  if ( ! NeedGhostSwap ) return;
534 
535  // clear flag indicating we need to do this ghost particle swap again
536  NeedGhostSwap = false;
537 
538  // delete all our current ghost particles; even if we have no local
539  // particles now, we may have pairlists left over from earlier when we did
540  // have local particles
541  PData.ghostDestroy(PData.getGhostNum(), 0);
542 
543  // find the number of nodes we need to communicate with
544  unsigned N = Ippl::getNodes();
545  unsigned sendnum = 0;
546  for (i=0; i < N; i++)
547  if (InterNodeList[i] && this->NodeCount[i] > 0)
548  sendnum++;
549 
550  // if there are no interaction nodes, we can just compute local pairlists
551  // and then return
552  if (sendnum == 0 || LocalNum == 0) {
553  return;
554  }
555 
556  // get the maximum interaction radius for the particles
557  // we actually check twice the radius
558  T interRad = 2.0 * getMaxInteractionRadius();
559 
560  // an NDRegion object used to store the interaction region of a particle
561  NDRegion<T,Dim> pLoc;
562 
563  // Ghost particles are swapped in one pass: an interaction region for a
564  // particle is created, and intersected with all the vnodes, and if the
565  // particle needs to go to that vnode, it is sent.
566 
567  // create new messages to send to our neighbors
568  for (i=0; i < N; i++)
569  if (InterNodeList[i] && this->NodeCount[i] > 0)
570  this->SwapMsgList[i] = new Message;
571 
572  // Go through the particles, find those with interaction radius
573  // which overlaps with a neighboring left node, and copy into a message.
574  // The interaction radius used to check for whether to send the particle
575  // is (max inter. radius of system)*2.
576 
577 
578  // for (i=0; i < LocalNum; ++i) {
579 
580  // initialize the flags which indicate which node the particle will be
581  // sent to
582 
583  // ada memset((void *)SentToNodeList, 0, N * sizeof(bool));
584 
585  // get the position of the ith particle, and form an NDRegion which
586  // is a cube with sides of length twice the interaction radius
587  // ada for (j=0; j < Dim; ++j)
588  // ada pLoc[j] = PRegion<T>(PData.R[i][j] - interRad,
589  // PData.R[i][j] + interRad);
590 
591  // see which Vnodes this postion is in; if none, it is local
592  // ada typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN = this->RLayout.touch_range_rdv(pLoc);
593  // ada typename RegionLayout<T,Dim,Mesh>::touch_iterator_dv tVNit = touchingVN.first;
594  // ada for ( ; tVNit != touchingVN.second; ++tVNit) {
595  // ada Rnode<T,Dim> *tVN = (*tVNit).second;
596  // ada unsigned node = tVN->getNode();
597 
598  // the node has been found - copy particle data into a message,
599  // ada if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
600  // ada if (! InterNodeList[node]) {
601  // ada ERRORMSG("ParticleCashedLayout: Cannot send ghost " << i);
602  // ada ERRORMSG(" to node " << node << " ... skipping." << endl);
603  // ada }
604  // ada else {
605  // ada PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
606  // ada SentToNodeList[node] = true;
607  // }
608  // }
609  // }
610  // }
611 
612  // send out messages with ghost particles
613 
614  /*
615  ada: buggy BUGGY node hangs in later receive_block
616 
617  int tag = Ippl::Comm->next_tag(P_SPATIAL_GHOST_TAG, P_LAYOUT_CYCLE);
618  for (i=0; i < N; ++i) {
619  if (InterNodeList[i] && this->NodeCount[i] > 0) {
620  // add a final 'zero' number of particles to indicate the end
621  PData.ghostPutMessage(*(this->SwapMsgList[i]), (unsigned)0, (unsigned)0);
622 
623  // send the message
624  Ippl::Comm->send(this->SwapMsgList[i], i, tag);
625  }
626  }
627  */
628 
629  // receive ghost particles from other nodes, and add them to our list
630 
631  /*
632  while (sendnum-- > 0) {
633  int node = Communicate::COMM_ANY_NODE;
634  unsigned oldGN = PData.getGhostNum();
635  Message *recmsg = Ippl::Comm->receive_block(node, tag);
636 
637  while (PData.ghostGetMessage(*recmsg, node) > 0);
638  delete recmsg;
639 
640  // find pairs with these ghost particles
641  find_pairs(LocalNum, LocalNum + oldGN, LocalNum + PData.getGhostNum(),
642  false, PData);
643  }
644  */
645 
646 }
647 
648 
650 // copy particles to other nodes to cashed particle container. The arguments
651 // are the current number of local particles, and the IpplParticleBase object.
652 // Make sure not to send any particles to, or receive particles from,
653 // nodes which have no particles on them.
654 //
655 // special version to take care of periodic boundaries
656 template < class T, unsigned Dim, class Mesh >
658  unsigned LocalNum,
660  const bool periodicBC[2*Dim])
661 {
662  unsigned int i, j; // loop variables
663  unsigned d;
664 
665  // if we've already swapped particles since the last update, we're done
666  if ( ! NeedGhostSwap ) return;
667 
668  // clear flag indicating we need to do this ghost particle swap again
669  NeedGhostSwap = false;
670 
671  // delete all our current ghost particles; even if we have no local
672  // particles now, we may have pairlists left over from earlier when we did
673  // have local particles
674  // FIXME: still necessary?
675  PData.ghostDestroy(PData.getGhostNum(), 0);
676 
677  // find the number of nodes we need to communicate with
678  unsigned N = Ippl::getNodes();
679  unsigned pe = Ippl::myNode();
680  unsigned sendnum = 0;
681  for (i=0; i < N; i++)
682  if (InterNodeList[i] && this->NodeCount[i] > 0)
683  sendnum++;
684 
685  // if there are no interaction nodes, we can just return
686  if (sendnum == 0 || LocalNum == 0)
687  return;
688 
689  // get the maximum interaction radius for the particles
690  // we actually check twice the radius
691  //FIXME: interRad = r - domain/2
692  T interRad = getMaxInteractionRadius(); // - this->RLayout.getDomain().getLocalNDIndex().length() / 2.0;
693 
694  //if(interRad < 0) return;
695 
696  // get domain info
697  const NDRegion<T,Dim>& globalDom = this->RLayout.getDomain();
698 
699  // some stuff for computing reflected domains
700  T offset[Dim];
701  unsigned numRef;
702  bool flipBit, activeBit[Dim], refBit[Dim];
703  NDRegion<T,Dim> pLoc, refLoc;
704 
705  // region layout iterators
706  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
707  typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN;
709  SingleParticlePos_t savePos; // save position of reflected ghosts
710 
711  // Ghost particles are swapped in one pass: an interaction region for a
712  // particle is created, and intersected with all the vnodes, and if the
713  // particle needs to go to that vnode, it is sent.
714 
715  // create new messages to send to our neighbors
716  for (i=0; i < N; i++)
717  if (InterNodeList[i] && this->NodeCount[i] > 0)
718  this->SwapMsgList[i] = new Message;
719 
720  // Go through the particles, find those with interaction radius
721  // which overlaps with a neighboring left node, and copy into a message.
722  // The interaction radius used to check for whether to send the particle
723  // is (max inter. radius of system)*2.
724  for (i=0; i < LocalNum; ++i) {
725 
726  // initialize flags indicating which nodes the particle has been sent to
727  memset((void *)SentToNodeList, 0, N * sizeof(bool));
728 
729  // get the position of the ith particle, and form an NDRegion which
730  // is a cube with sides of length twice the interaction radius
731  for (j=0; j < Dim; ++j)
732  pLoc[j] = PRegion<T>(PData.R[i][j] - interRad,
733  PData.R[i][j] + interRad);
734 
735 
736  // see which Vnodes this postion is in; if none, it is local
737  touchingVN = this->RLayout.touch_range_rdv(pLoc);
738  for (tVNit = touchingVN.first; tVNit != touchingVN.second; ++tVNit) {
739  Rnode<T,Dim> *tVN = (*tVNit).second;
740  unsigned node = tVN->getNode();
741 
742  // the node has been found - copy particle data into a message,
743  if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
744  if (! InterNodeList[node]) {
745  ERRORMSG("ParticleCashedLayout: Cannot send ghost " << i);
746  ERRORMSG(" to node " << node << " ... skipping." << endl);
747  }
748  else {
749  PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
750  SentToNodeList[node] = true;
751  }
752  }
753  }
754 
755  // look for boundary crossings and check reflected domains
756  numRef = 0;
757  for (d=0; d<Dim; ++d) {
758  if (periodicBC[2*d] && pLoc[d].first()<globalDom[d].first()) {
759  // crossed lower boundary
760  offset[d] = globalDom[d].length();
761  activeBit[d] = true;
762  numRef = 2 * numRef + 1;
763  }
764  else if (periodicBC[2*d+1] && pLoc[d].last()>globalDom[d].last()) {
765  // crossed upper boundary
766  offset[d] = -globalDom[d].length();
767  activeBit[d] = true;
768  numRef = 2 * numRef + 1;
769  }
770  else {
771  offset[d] = 0.0;
772  activeBit[d] = false;
773  }
774  refBit[d] = false; // reset bools indicating reflecting dims
775  }
776 
777  if (numRef>0) savePos = PData.R[i]; // copy current particle position
778 
779  // loop over reflected domains
780  for (j=0; j<numRef; ++j) {
781  // set up reflected neighborhood and position
782  refLoc = pLoc;
783  PData.R[i] = savePos;
784  // find next combination of dimension offsets
785  d = 0;
786  flipBit = false;
787  while (d<Dim && !flipBit) {
788  // first check if this dim is active
789  if (activeBit[d]) {
790  // now flip bit for this dimension
791  if (refBit[d]) {
792  // flip this bit off and proceed to next dim
793  refBit[d] = false;
794  }
795  else { // refBit[d] is off
796  // flip this bit on and indicate we're done
797  refBit[d] = true;
798  flipBit = true;
799  }
800  }
801  ++d;
802  }
803  PAssert(flipBit); // check that we found next combination
804 
805  // now offset the reflected neighborhood and particle position
806  for (d=0; d<Dim; ++d) {
807  if (refBit[d]) {
808  refLoc[d] = refLoc[d] + offset[d];
809  PData.R[i][d] = PData.R[i][d] + offset[d];
810  }
811  }
812 
813  // initialize flags indicating which nodes the particle has been sent to
814  memset((void *)SentToNodeList, 0, N * sizeof(bool));
815 
816  // see which Vnodes this postion is in; if none, it is local
817  touchingVN = this->RLayout.touch_range_rdv(refLoc);
818  for (tVNit = touchingVN.first; tVNit != touchingVN.second; ++tVNit) {
819  Rnode<T,Dim> *tVN = (*tVNit).second;
820  unsigned node = tVN->getNode();
821 
822  // the node has been found - copy particle data into a message,
823  if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
824  if (! InterNodeList[node]) {
825  ERRORMSG("ParticleCashedLayout: Cannot send ghost " << i);
826  ERRORMSG(" to node " << node << " ... skipping." << endl);
827  }
828  else {
829  PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
830  SentToNodeList[node] = true;
831  }
832  }
833  }
834 
835  //FIXME: NO LOCAL GHOST NODES!
836  if (InterNodeList[pe]) { // we may interact with local domains
837  // for reflected domains, we also must check against local domains
838  bool interact = false;
839  localVN = this->RLayout.begin_iv();
840  while (localVN != endLocalVN && !interact) {
841  interact = refLoc.touches((*localVN).second->getDomain());
842  ++localVN;
843  }
844  if (interact) {
845  PData.ghostPutMessage(*(this->SwapMsgList[pe]), 1, i);
846  }
847  }
848  }
849  if (numRef>0) PData.R[i] = savePos; // restore particle position data
850 
851  }
852 
853  // send out messages with ghost particles
855  for (i=0; i < N; ++i) {
856  if (InterNodeList[i] && this->NodeCount[i] > 0) {
857  // add a final 'zero' number of particles to indicate the end
858  PData.ghostPutMessage(*(this->SwapMsgList[i]), (unsigned)0, (unsigned)0);
859 
860  // send the message
861  Ippl::Comm->send(this->SwapMsgList[i], i, tag);
862  }
863  }
864 
865  // receive ghost particles from other nodes, and add them to our list
866  while (sendnum-- > 0) {
867  int node = Communicate::COMM_ANY_NODE;
868  Message *recmsg = Ippl::Comm->receive_block(node, tag);
869 
870  while (PData.ghostGetMessage(*recmsg, node) > 0);
871  delete recmsg;
872 
873  }
874  //FIXME: ghost parts now in ghost holders of PData?
875 
876 }
877 
879 // print it out
880 template < class T, unsigned Dim, class Mesh >
881 std::ostream& operator<<(std::ostream& out, const ParticleCashedLayout<T,Dim,Mesh>& L) {
882 
883  out << "ParticleCashedLayout, with particle distribution:\n ";
884  for (unsigned int i=0; i < (unsigned int) Ippl::getNodes(); ++i)
885  out << L.getNodeCount(i) << " ";
886  out << "\nInteractLayout decomposition = " << L.getLayout();
887  return out;
888 }
889 
890 
892 // Repartition onto a new layout, if the layout changes ... this is a
893 // virtual function called by a UserList, as opposed to the RepartitionLayout
894 // function used by the particle load balancing mechanism.
895 template < class T, unsigned Dim, class Mesh >
897 
898  // perform actions to restructure our data due to a change in the
899  // RegionLayout
900  if (userlist->getUserListID() == this->RLayout.get_Id()) {
901  // clear out current interaction node storage; if the next update
902  // indicates we have a non-zero interaction radius, this info will be
903  // rebuilt (by calling rebuild_interaction_data)
904  InteractionNodes = 0;
905  setMaxInteractionRadius(0);
906  NeedGhostSwap = true;
907  }
908 }
const unsigned Dim
#define SWAP(a, b, type)
Definition: fftpack.cpp:83
#define P_SPATIAL_GHOST_TAG
Definition: Tags.h:83
#define P_LAYOUT_CYCLE
Definition: Tags.h:86
#define P_SPATIAL_RETURN_TAG
Definition: Tags.h:81
#define P_SPATIAL_LAYOUT_TAG
Definition: Tags.h:80
T ParticlePeriodicBCond(const T t, const T minval, const T maxval)
std::ostream & operator<<(std::ostream &out, const ParticleCashedLayout< T, Dim, Mesh > &L)
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
#define PAssert(c)
Definition: PAssert.h:102
Definition: Mesh.h:35
bool send(Message *, int node, int tag, bool delmsg=true)
virtual int broadcast_others(Message *, int, bool delmsg=true)
Message * receive_block(int &node, int &tag)
Message & put(const T &val)
Definition: Message.h:406
Message & get(const T &cval)
Definition: Message.h:476
int next_tag(int t, int s=1000)
Definition: TagMaker.h:39
int getNodeCount(unsigned i) const
RegionLayout< T, Dim, Mesh > & getLayout()
void getCashedParticles(IpplParticleBase< ParticleCashedLayout< T, Dim, Mesh > > &)
void swap_ghost_particles(unsigned, IpplParticleBase< ParticleCashedLayout< T, Dim, Mesh > > &)
void update(IpplParticleBase< ParticleCashedLayout< T, Dim, Mesh > > &p, const ParticleAttrib< char > *canSwap=0)
virtual void Repartition(UserList *)
bool touches(const NDRegion< T, Dim > &nr) const
Definition: NDRegion.h:175
iterator_iv begin_iv()
Definition: RegionLayout.h:146
touch_range_dv touch_range_rdv(const NDRegion< T, Dim > &domain)
Definition: RegionLayout.h:159
ac_id_vnodes::iterator iterator_iv
Definition: RegionLayout.h:66
std::pair< touch_iterator_dv, touch_iterator_dv > touch_range_dv
Definition: RegionLayout.h:71
Definition: Rnode.h:30
int getNode()
Definition: Rnode.h:52
static int getNodes()
Definition: IpplInfo.cpp:670
static int myNode()
Definition: IpplInfo.cpp:691
static Communicate * Comm
Definition: IpplInfo.h:84
ID_t getUserListID() const
Definition: UserList.cpp:54