OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
OPAL
ParticleInteractLayout.hpp
Go to the documentation of this file.
1 //
2 // Class ParticleInteractLayout
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 
32 // constructor, from a FieldLayout
33 template < class T, unsigned Dim, class Mesh >
35  fl)
37  setup(); // perform necessary setup
38 }
39 
40 
42 // constructor, from a FieldLayout
43 template < class T, unsigned Dim, class Mesh >
45  fl, Mesh& mesh)
46  : ParticleSpatialLayout<T,Dim,Mesh>(fl,mesh) {
47  setup(); // perform necessary setup
48 }
49 
50 
52 // constructor, from a RegionLayout
53 template < class T, unsigned Dim, class Mesh >
56  setup(); // perform necessary setup
57 }
58 
59 
61 // default constructor ... this does not initialize the RegionLayout,
62 // it will be instead initialized during the first update.
63 template < class T, unsigned Dim, class Mesh >
66  setup(); // perform necessary setup
67 }
68 
69 
71 // perform common constructor tasks
72 template < class T, unsigned Dim, class Mesh >
74 
75  // create storage for message pointers used in swapping particles
76  unsigned N = Ippl::getNodes();
77  InterNodeList = new bool[N];
78  SentToNodeList = new bool[N];
79  InteractionNodes = 0;
80 
81  // initialize interaction radius information
82  InterRadius = 0;
83  InterRadiusArray = 0;
84  MaxGlobalInterRadius = 0;
85 }
86 
87 
89 // destructor
90 template < class T, unsigned Dim, class Mesh >
92 
93  delete [] InterNodeList;
94  delete [] SentToNodeList;
95 
96  for (int i=(PairList.size() - 1); i >= 0; --i)
97  delete (PairList[i]);
98 }
99 
100 
102 // Return the maximum interaction radius of the local particles.
103 template < class T, unsigned Dim, class Mesh >
105 
106  if (InterRadiusArray != 0) {
107  if (InterRadiusArray->size() > 0)
108  return *(max_element(InterRadiusArray->begin(),
109  InterRadiusArray->end()));
110  else
111  return 0.0;
112  } else {
113  return InterRadius;
114  }
115 }
116 
117 
119 // Retrieve a Forward-style iterator for the beginning and end of the
120 // Nth (local) particle's nearest-neighbor pairlist.
121 // If this is the first call of this
122 // method after update(), this must make sure up-to-date info on particles
123 // from neighboring nodes is available.
124 template < class T, unsigned Dim, class Mesh >
126  pair_iterator& bpi, pair_iterator& epi,
128 
129  // check if we have any particle boundary conditions
130  if (getUpdateFlag(ParticleLayout<T,Dim>::BCONDS)) {
131  // check which boundaries, if any, are periodic
132  ParticleBConds<T,Dim>& pBConds = this->getBConds();
133  bool periodicBC[2*Dim];
134  unsigned numPeriodicBC = 0;
136  for (unsigned d=0; d<2*Dim; ++d) {
137  periodicBC[d] = (pBConds[d] == periodicBCond);
138  if (periodicBC[d]) ++numPeriodicBC;
139  }
140  if (numPeriodicBC>0) {
141  // we need to reflect domains across all periodic boundaries
142  // call specialized function to update ghost particle data
143  swap_ghost_particles(PData.getLocalNum(), PData, periodicBC);
144  }
145  else { // no periodic boundaries, call standard function
146  swap_ghost_particles(PData.getLocalNum(), PData);
147  }
148  }
149  else { // no boundary conditions, call standard function
150  // update ghost particle data if necessary ... this will also build
151  // the pairlists if needed
152  swap_ghost_particles(PData.getLocalNum(), PData);
153  }
154 
155  // get iterators for Nth particle's pairlist ... no check for array sub.
156  bpi = PairList[n]->begin();
157  epi = PairList[n]->end();
158 
159  return;
160 }
161 
162 
164 // for each dimension, calculate where neighboring Vnodes and physical
165 // nodes are located, and which nodes are within interaction distance
166 // of our own Vnodes. Save this info for use in sending ghost particles.
167 template < class T, unsigned Dim, class Mesh >
169 
170  unsigned int j, d; // loop variables
171 
172  // initialize data about interaction nodes, and get the inter radius
173  InteractionNodes = 0;
174  T interRad = 2.0 * getMaxInteractionRadius();
175 
176  // initialize the message list and initial node count
177  unsigned N = Ippl::getNodes();
178  for (j=0; j < N; ++j)
179  InterNodeList[j] = false;
180 
181  // if no interaction radius, we're done
182  if (interRad <= 0.0)
183  return;
184 
185  // get RegionLayout iterators
186  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
187  // determine which physical nodes are in our interaction domain
188  for (localVN = this->RLayout.begin_iv(); localVN != endLocalVN; ++localVN) {
189  // for each local Vnode, the domain to check equals the local Vnode dom
190  // plus twice the interaction radius
191  NDRegion<T,Dim> chkDom((*localVN).second->getDomain());
192  for (d=0; d < Dim; ++d) {
193  chkDom[d] = PRegion<T>(chkDom[d].first() - interRad,
194  chkDom[d].last() + interRad);
195  }
196 
197  // use the RegionLayout to find all remote Vnodes which touch the domain
198  // being checked here
199  typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
200  this->RLayout.touch_range_rdv(chkDom);
201  typename RegionLayout<T,Dim,Mesh>::touch_iterator_dv tVN = touchingVN.first;
202  for ( ; tVN != touchingVN.second; ++tVN) {
203  // note that we need to send a message to the node which contains
204  // this remote Vnode
205  unsigned vn = ((*tVN).second)->getNode();
206  if ( ! InterNodeList[vn] ) {
207  InterNodeList[vn] = true;
208  InteractionNodes++;
209  }
210  }
211  }
212 
213  // set the flag indicating the swap ghost particle routine should
214  // be called the next time we try to access a pairlist or do anything
215  // of utility with the ghost particles
216  NeedGhostSwap = true;
217  return;
218 }
219 
220 
222 // for each dimension, calculate where neighboring Vnodes and physical
223 // nodes are located, and which nodes are within interaction distance
224 // of our own Vnodes. Save this info for use in sending ghost particles.
225 // Special version to handle periodic boundary conditions
226 template < class T, unsigned Dim, class Mesh >
228  const bool periodicBC[2*Dim])
229 {
230  unsigned int j, d; // loop variables
231  unsigned pe = Ippl::myNode();
232 
233  // initialize data about interaction nodes, and get the inter radius
234  InteractionNodes = 0;
235  T interRad = 2.0 * getMaxInteractionRadius();
236 
237  // initialize the message list and initial node count
238  unsigned N = Ippl::getNodes();
239  for (j=0; j < N; ++j)
240  InterNodeList[j] = false;
241 
242  // if no interaction radius, we're done
243  if (interRad <= 0.0)
244  return;
245 
246  // get domain info
247  const NDRegion<T,Dim>& globalDom = this->RLayout.getDomain();
248 
249  // some stuff for computing reflected domains
250  T offset[Dim];
251  unsigned numRef;
252  bool flipBit, activeBit[Dim], refBit[Dim];
253  NDRegion<T,Dim> chkDom, refDom;
254 
255  // get RegionLayout iterators
256  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
257  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN2;
258  typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN;
260 
261  // determine which physical nodes are in our interaction domain
262  for (localVN = this->RLayout.begin_iv(); localVN != endLocalVN; ++localVN) {
263  // for each local Vnode, the domain to check equals the local Vnode dom
264  // plus twice the interaction radius
265  chkDom = (*localVN).second->getDomain();
266  for (d=0; d<Dim; ++d) {
267  chkDom[d] = PRegion<T>(chkDom[d].first() - interRad,
268  chkDom[d].last() + interRad);
269  }
270 
271  // use the RegionLayout to find all remote Vnodes which touch
272  // the domain being checked here
273  touchingVN = this->RLayout.touch_range_rdv(chkDom);
274  for (tVN = touchingVN.first; tVN != touchingVN.second; ++tVN) {
275  // note that we need to send a message to the node which contains
276  // this remote Vnode
277  unsigned vn = ((*tVN).second)->getNode();
278  if ( ! InterNodeList[vn] ) {
279  InterNodeList[vn] = true;
280  InteractionNodes++;
281  }
282  }
283 
284  // look for boundary crossings and check reflected domains
285  numRef = 0;
286  for (d=0; d<Dim; ++d) {
287  if (periodicBC[2*d] && chkDom[d].first()<globalDom[d].first()) {
288  // crossed lower boundary
289  offset[d] = globalDom[d].length();
290  activeBit[d] = true;
291  numRef = 2 * numRef + 1;
292  }
293  else if (periodicBC[2*d+1] && chkDom[d].last()>globalDom[d].last()) {
294  // crossed upper boundary
295  offset[d] = -globalDom[d].length();
296  activeBit[d] = true;
297  numRef = 2 * numRef + 1;
298  }
299  else {
300  offset[d] = 0.0;
301  activeBit[d] = false;
302  }
303  refBit[d] = false; // reset reflected domain bools
304  }
305 
306  // compute and check each domain reflection
307  for (j=0; j<numRef; ++j) {
308  // set up reflected domain: first initialize to original domain
309  refDom = chkDom;
310  // find next combination of dimension offsets
311  d = 0;
312  flipBit = false;
313  while (d<Dim && !flipBit) {
314  // first check if this dim is active
315  if (activeBit[d]) {
316  // now flip bit for this dimension
317  if (refBit[d]) {
318  // flip this bit off and proceed to next dim
319  refBit[d] = false;
320  }
321  else { // refBit[d] is off
322  // flip this bit on and indicate we're done
323  refBit[d] = true;
324  flipBit = true;
325  }
326  }
327  ++d;
328  }
329  PAssert(flipBit); // check that we found next combination
330 
331  // now offset the reflected domain
332  for (d=0; d<Dim; ++d) {
333  if (refBit[d]) refDom[d] = refDom[d] + offset[d];
334  }
335 
336  // use the RegionLayout to find all remote Vnodes which touch
337  // the domain being checked here
338  touchingVN = this->RLayout.touch_range_rdv(refDom);
339  for (tVN = touchingVN.first; tVN != touchingVN.second; ++tVN) {
340  // note that we need to send a message to the node which contains
341  // this remote Vnode
342  unsigned vn = ((*tVN).second)->getNode();
343  if ( ! InterNodeList[vn] ) {
344  InterNodeList[vn] = true;
345  InteractionNodes++;
346  }
347  }
348 
349  if (!InterNodeList[pe]) { // check if we interact with our own domains
350  // for reflected domains, we also must check against local domains
351  bool interact = false;
352  localVN2 = this->RLayout.begin_iv();
353  while (localVN2 != endLocalVN && !interact) {
354  interact = refDom.touches((*localVN2).second->getDomain());
355  ++localVN2;
356  }
357  if (interact) {
358  InterNodeList[pe] = true;
359  InteractionNodes++;
360  }
361  }
362  }
363 
364  }
365 
366  // set the flag indicating the swap ghost particle routine should
367  // be called the next time we try to access a pairlist or do anything
368  // of utility with the ghost particles
369  NeedGhostSwap = true;
370  return;
371 }
372 
373 
375 // Update the location and indices of all atoms in the given IpplParticleBase
376 // object. This handles swapping particles among processors if
377 // needed, and handles create and destroy requests. When complete,
378 // all nodes have correct layout information.
379 template < class T, unsigned Dim, class Mesh >
382  const ParticleAttrib<char>* canSwap) {
383 
384  unsigned N = Ippl::getNodes();
385  unsigned myN = Ippl::myNode();
386  unsigned LocalNum = PData.getLocalNum();
387  unsigned DestroyNum = PData.getDestroyNum();
388  unsigned TotalNum;
389  T maxrad = getMaxLocalInteractionRadius();
390  int node;
391 
392  // delete particles in destroy list, update local num
393  PData.performDestroy();
394  LocalNum -= DestroyNum;
395 
396  // set up our layout, if not already done ... we could also do this if
397  // we needed to expand our spatial region.
398  if ( ! this->RLayout.initialized())
399  rebuild_layout(LocalNum,PData);
400 
401  // apply boundary conditions to the particle positions
402  if (getUpdateFlag(ParticleLayout<T,Dim>::BCONDS))
403  apply_bconds(LocalNum, PData.R, this->getBConds(), this->RLayout.getDomain());
404 
405  // Now we can swap particles that have moved outside the region of
406  // local field space. This is done in several passes, one for each
407  // spatial dimension. The NodeCount values are updated by this routine.
408  if (N > 1 && getUpdateFlag(this->SWAP)) {
409  if (canSwap==0)
410  LocalNum = swap_particles(LocalNum, PData);
411  else
412  LocalNum = swap_particles(LocalNum, PData, *canSwap);
413  }
414 
415  // flag we need to update our ghost particles
416  NeedGhostSwap = true;
417 
418  // Save how many local particles we have.
419  TotalNum = this->NodeCount[myN] = LocalNum;
420 
421  // there is extra work to do if there are multipple nodes, to distribute
422  // the particle layout data to all nodes
423  if (N > 1) {
424  // At this point, we can send our particle count updates to node 0, and
425  // receive back the particle layout.
428  if (myN != 0) {
429  Message *msg = new Message;
430 
431  // put local particle count in the message
432  msg->put(LocalNum);
433 
434  // also put in our maximum interaction radius
435  msg->put(maxrad);
436 
437  // send this info to node 0
438  Ippl::Comm->send(msg, 0, tag1);
439 
440  // receive back the number of particles on each node, and the maximum
441  // interaction radius
442  node = 0;
443  msg = Ippl::Comm->receive_block(node, tag2);
444  msg->get(this->NodeCount);
445  msg->get(maxrad);
446  msg->get(TotalNum);
447  } else { // do update tasks particular to node 0
448  // receive messages from other nodes describing what they have
449  int notrecvd = N - 1; // do not need to receive from node 0
450  TotalNum = LocalNum;
451  while (notrecvd > 0) {
452  // receive a message from another node. After recv, node == sender.
454  Message *msg = Ippl::Comm->receive_block(node, tag1);
455  int remNodeCount = 0;
456  T remMaxRad = 0.0;
457  msg->get(remNodeCount);
458  msg->get(remMaxRad);
459  delete msg;
460  notrecvd--;
461 
462  // update values based on data from remote node
463  TotalNum += remNodeCount;
464  this->NodeCount[node] = remNodeCount;
465  if (remMaxRad > maxrad)
466  maxrad = remMaxRad;
467  }
468 
469  // send info back to all the client nodes
470  Message *msg = new Message;
471  msg->put(this->NodeCount, this->NodeCount + N);
472  msg->put(maxrad);
473  msg->put(TotalNum);
474  Ippl::Comm->broadcast_others(msg, tag2);
475  }
476  }
477 
478  // update our particle number counts
479  PData.setTotalNum(TotalNum); // set the total atom count
480  PData.setLocalNum(LocalNum); // set the number of local atoms
481 
482  // if the interaction radius changed, must recalculate some things
483  if (maxrad != getMaxInteractionRadius()) {
484  setMaxInteractionRadius(maxrad);
485  // check if we have any particle boundary conditions
486  if (getUpdateFlag(ParticleLayout<T,Dim>::BCONDS)) {
487  // check which boundaries, if any, are periodic
488  ParticleBConds<T,Dim>& pBConds = this->getBConds();
489  bool periodicBC[2*Dim];
490  unsigned numPeriodicBC = 0;
492  for (unsigned d=0; d<2*Dim; ++d) {
493  periodicBC[d] = (pBConds[d] == periodicBCond);
494  if (periodicBC[d]) ++numPeriodicBC;
495  }
496  if (numPeriodicBC>0) {
497  // we need to reflect domains across all periodic boundaries
498  // call specialized function
499  rebuild_interaction_data(periodicBC);
500  }
501  else { // no periodic boundaries, call standard function
502  rebuild_interaction_data();
503  }
504  }
505  else { // no boundary conditions, call standard function
506  rebuild_interaction_data();
507  }
508  }
509  return;
510 }
511 
512 
514 // copy particles to other nodes for pairlist computation. The arguments
515 // are the current number of local particles, and the ParticleBase object.
516 // Make sure not to send any particles to, or receive particles from,
517 // nodes which have no particles on them. This also takes care of
518 // building the pairlists.
519 template < class T, unsigned Dim, class Mesh >
521  LocalNum,
523 
524  unsigned int i; // loop variables
525 
526  // if we've already swapped particles since the last update, we're done
527  if ( ! NeedGhostSwap ) return;
528 
529  // clear flag indicating we need to do this ghost particle swap again
530  NeedGhostSwap = false;
531 
532  // delete all our current ghost particles; even if we have no local
533  // particles now, we may have pairlists left over from earlier when we did
534  // have local particles
535  PData.ghostDestroy(PData.getGhostNum(), 0);
536 
537  // find the number of nodes we need to communicate with
538  unsigned N = Ippl::getNodes();
539  unsigned sendnum = 0;
540  for (i=0; i < N; i++)
541  if (InterNodeList[i] && this->NodeCount[i] > 0)
542  sendnum++;
543 
544  // if there are no interaction nodes, we can just compute local pairlists
545  // and then return
546  if (sendnum == 0 || LocalNum == 0) {
547  find_pairs(LocalNum, 0, LocalNum, true, PData);
548  return;
549  }
550 
551  // get the maximum interaction radius for the particles
552  // we actually check twice the radius
553  T interRad = 2.0 * getMaxInteractionRadius();
554 
555  // an NDRegion object used to store the interaction region of a particle
556  NDRegion<T,Dim> pLoc;
557 
558  // Ghost particles are swapped in one pass: an interaction region for a
559  // particle is created, and intersected with all the vnodes, and if the
560  // particle needs to go to that vnode, it is sent.
561 
562  // create new messages to send to our neighbors
563  for (i=0; i < N; i++)
564  if (InterNodeList[i] && this->NodeCount[i] > 0)
565  this->SwapMsgList[i] = new Message;
566 
567  // Go through the particles, find those with interaction radius
568  // which overlaps with a neighboring left node, and copy into a message.
569  // The interaction radius used to check for whether to send the particle
570  // is (max inter. radius of system)*2.
571 
572 
573  // for (i=0; i < LocalNum; ++i) {
574 
575  // initialize the flags which indicate which node the particle will be
576  // sent to
577 
578  // ada memset((void *)SentToNodeList, 0, N * sizeof(bool));
579 
580  // get the position of the ith particle, and form an NDRegion which
581  // is a cube with sides of length twice the interaction radius
582  // ada for (j=0; j < Dim; ++j)
583  // ada pLoc[j] = PRegion<T>(PData.R[i][j] - interRad,
584  // PData.R[i][j] + interRad);
585 
586  // see which Vnodes this postion is in; if none, it is local
587  // ada typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN = this->RLayout.touch_range_rdv(pLoc);
588  // ada typename RegionLayout<T,Dim,Mesh>::touch_iterator_dv tVNit = touchingVN.first;
589  // ada for ( ; tVNit != touchingVN.second; ++tVNit) {
590  // ada Rnode<T,Dim> *tVN = (*tVNit).second;
591  // ada unsigned node = tVN->getNode();
592 
593  // the node has been found - copy particle data into a message,
594  // ada if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
595  // ada if (! InterNodeList[node]) {
596  // ada ERRORMSG("ParticleInteractLayout: Cannot send ghost " << i);
597  // ada ERRORMSG(" to node " << node << " ... skipping." << endl);
598  // ada }
599  // ada else {
600  // ada PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
601  // ada SentToNodeList[node] = true;
602  // }
603  // }
604  // }
605  // }
606 
607  // send out messages with ghost particles
608 
609  /*
610  ada: buggy BUGGY node hangs in later receive_block
611 
612  int tag = Ippl::Comm->next_tag(P_SPATIAL_GHOST_TAG, P_LAYOUT_CYCLE);
613  for (i=0; i < N; ++i) {
614  if (InterNodeList[i] && this->NodeCount[i] > 0) {
615  // add a final 'zero' number of particles to indicate the end
616  PData.ghostPutMessage(*(this->SwapMsgList[i]), (unsigned)0, (unsigned)0);
617 
618  // send the message
619  Ippl::Comm->send(this->SwapMsgList[i], i, tag);
620  }
621  }
622  */
623 
624  // while we're waiting for messages to arrive, calculate our local pairs
625  find_pairs(LocalNum, 0, LocalNum, true, PData);
626 
627  // receive ghost particles from other nodes, and add them to our list
628 
629  /*
630  while (sendnum-- > 0) {
631  int node = Communicate::COMM_ANY_NODE;
632  unsigned oldGN = PData.getGhostNum();
633  Message *recmsg = Ippl::Comm->receive_block(node, tag);
634 
635  while (PData.ghostGetMessage(*recmsg, node) > 0);
636  delete recmsg;
637 
638  // find pairs with these ghost particles
639  find_pairs(LocalNum, LocalNum + oldGN, LocalNum + PData.getGhostNum(),
640  false, PData);
641  }
642  */
643 
644 }
645 
646 
648 // copy particles to other nodes for pairlist computation. The arguments
649 // are the current number of local particles, and the IpplParticleBase object.
650 // Make sure not to send any particles to, or receive particles from,
651 // nodes which have no particles on them. This also takes care of
652 // building the pairlists.
653 // special version to take care of periodic boundaries
654 template < class T, unsigned Dim, class Mesh >
656  unsigned LocalNum,
658  const bool periodicBC[2*Dim])
659 {
660  unsigned int i, j; // loop variables
661  unsigned d;
662 
663  // if we've already swapped particles since the last update, we're done
664  if ( ! NeedGhostSwap ) return;
665 
666  // clear flag indicating we need to do this ghost particle swap again
667  NeedGhostSwap = false;
668 
669  // delete all our current ghost particles; even if we have no local
670  // particles now, we may have pairlists left over from earlier when we did
671  // have local particles
672  PData.ghostDestroy(PData.getGhostNum(), 0);
673 
674  // find the number of nodes we need to communicate with
675  unsigned N = Ippl::getNodes();
676  unsigned pe = Ippl::myNode();
677  unsigned sendnum = 0;
678  for (i=0; i < N; i++)
679  if (InterNodeList[i] && this->NodeCount[i] > 0)
680  sendnum++;
681 
682  // if there are no interaction nodes, we can just compute local pairlists
683  // and then return
684  if (sendnum == 0 || LocalNum == 0) {
685  find_pairs(LocalNum, 0, LocalNum, true, PData);
686  return;
687  }
688 
689  // get the maximum interaction radius for the particles
690  // we actually check twice the radius
691  T interRad = 2.0 * getMaxInteractionRadius();
692 
693  // get domain info
694  const NDRegion<T,Dim>& globalDom = this->RLayout.getDomain();
695 
696  // some stuff for computing reflected domains
697  T offset[Dim];
698  unsigned numRef;
699  bool flipBit, activeBit[Dim], refBit[Dim];
700  NDRegion<T,Dim> pLoc, refLoc;
701 
702  // region layout iterators
703  typename RegionLayout<T,Dim,Mesh>::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
704  typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN;
706  SingleParticlePos_t savePos; // save position of reflected ghosts
707 
708  // Ghost particles are swapped in one pass: an interaction region for a
709  // particle is created, and intersected with all the vnodes, and if the
710  // particle needs to go to that vnode, it is sent.
711 
712  // create new messages to send to our neighbors
713  for (i=0; i < N; i++)
714  if (InterNodeList[i] && this->NodeCount[i] > 0)
715  this->SwapMsgList[i] = new Message;
716 
717  // Go through the particles, find those with interaction radius
718  // which overlaps with a neighboring left node, and copy into a message.
719  // The interaction radius used to check for whether to send the particle
720  // is (max inter. radius of system)*2.
721  for (i=0; i < LocalNum; ++i) {
722 
723  // initialize flags indicating which nodes the particle has been sent to
724  memset((void *)SentToNodeList, 0, N * sizeof(bool));
725 
726  // get the position of the ith particle, and form an NDRegion which
727  // is a cube with sides of length twice the interaction radius
728  for (j=0; j < (unsigned int) Dim; ++j)
729  pLoc[j] = PRegion<T>(PData.R[i][j] - interRad,
730  PData.R[i][j] + interRad);
731 
732  // see which Vnodes this postion is in; if none, it is local
733  touchingVN = this->RLayout.touch_range_rdv(pLoc);
734  for (tVNit = touchingVN.first; tVNit != touchingVN.second; ++tVNit) {
735  Rnode<T,Dim> *tVN = (*tVNit).second;
736  unsigned node = tVN->getNode();
737 
738  // the node has been found - copy particle data into a message,
739  if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
740  if (! InterNodeList[node]) {
741  ERRORMSG("ParticleInteractLayout: Cannot send ghost " << i);
742  ERRORMSG(" to node " << node << " ... skipping." << endl);
743  }
744  else {
745  PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
746  SentToNodeList[node] = true;
747  }
748  }
749  }
750 
751  // look for boundary crossings and check reflected domains
752  numRef = 0;
753  for (d=0; d<Dim; ++d) {
754  if (periodicBC[2*d] && pLoc[d].first()<globalDom[d].first()) {
755  // crossed lower boundary
756  offset[d] = globalDom[d].length();
757  activeBit[d] = true;
758  numRef = 2 * numRef + 1;
759  }
760  else if (periodicBC[2*d+1] && pLoc[d].last()>globalDom[d].last()) {
761  // crossed upper boundary
762  offset[d] = -globalDom[d].length();
763  activeBit[d] = true;
764  numRef = 2 * numRef + 1;
765  }
766  else {
767  offset[d] = 0.0;
768  activeBit[d] = false;
769  }
770  refBit[d] = false; // reset bools indicating reflecting dims
771  }
772 
773  if (numRef>0) savePos = PData.R[i]; // copy current particle position
774 
775  // loop over reflected domains
776  for (j=0; j<numRef; ++j) {
777  // set up reflected neighborhood and position
778  refLoc = pLoc;
779  PData.R[i] = savePos;
780  // find next combination of dimension offsets
781  d = 0;
782  flipBit = false;
783  while (d<Dim && !flipBit) {
784  // first check if this dim is active
785  if (activeBit[d]) {
786  // now flip bit for this dimension
787  if (refBit[d]) {
788  // flip this bit off and proceed to next dim
789  refBit[d] = false;
790  }
791  else { // refBit[d] is off
792  // flip this bit on and indicate we're done
793  refBit[d] = true;
794  flipBit = true;
795  }
796  }
797  ++d;
798  }
799  PAssert(flipBit); // check that we found next combination
800 
801  // now offset the reflected neighborhood and particle position
802  for (d=0; d<Dim; ++d) {
803  if (refBit[d]) {
804  refLoc[d] = refLoc[d] + offset[d];
805  PData.R[i][d] = PData.R[i][d] + offset[d];
806  }
807  }
808 
809  // initialize flags indicating which nodes the particle has been sent to
810  memset((void *)SentToNodeList, 0, N * sizeof(bool));
811 
812  // see which Vnodes this postion is in; if none, it is local
813  touchingVN = this->RLayout.touch_range_rdv(refLoc);
814  for (tVNit = touchingVN.first; tVNit != touchingVN.second; ++tVNit) {
815  Rnode<T,Dim> *tVN = (*tVNit).second;
816  unsigned node = tVN->getNode();
817 
818  // the node has been found - copy particle data into a message,
819  if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
820  if (! InterNodeList[node]) {
821  ERRORMSG("ParticleInteractLayout: Cannot send ghost " << i);
822  ERRORMSG(" to node " << node << " ... skipping." << endl);
823  }
824  else {
825  PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
826  SentToNodeList[node] = true;
827  }
828  }
829  }
830 
831  if (InterNodeList[pe]) { // we may interact with local domains
832  // for reflected domains, we also must check against local domains
833  bool interact = false;
834  localVN = this->RLayout.begin_iv();
835  while (localVN != endLocalVN && !interact) {
836  interact = refLoc.touches((*localVN).second->getDomain());
837  ++localVN;
838  }
839  if (interact) {
840  PData.ghostPutMessage(*(this->SwapMsgList[pe]), 1, i);
841  }
842  }
843  }
844  if (numRef>0) PData.R[i] = savePos; // restore particle position data
845 
846  }
847 
848  // send out messages with ghost particles
850  for (i=0; i < N; ++i) {
851  if (InterNodeList[i] && this->NodeCount[i] > 0) {
852  // add a final 'zero' number of particles to indicate the end
853  PData.ghostPutMessage(*(this->SwapMsgList[i]), (unsigned)0, (unsigned)0);
854 
855  // send the message
856  Ippl::Comm->send(this->SwapMsgList[i], i, tag);
857  }
858  }
859 
860  // while we're waiting for messages to arrive, calculate our local pairs
861  find_pairs(LocalNum, 0, LocalNum, true, PData);
862 
863  // receive ghost particles from other nodes, and add them to our list
864  while (sendnum-- > 0) {
865  int node = Communicate::COMM_ANY_NODE;
866  unsigned oldGN = PData.getGhostNum();
867  Message *recmsg = Ippl::Comm->receive_block(node, tag);
868 
869  while (PData.ghostGetMessage(*recmsg, node) > 0);
870  delete recmsg;
871 
872  // find pairs with these ghost particles
873  find_pairs(LocalNum, LocalNum + oldGN, LocalNum + PData.getGhostNum(),
874  false, PData);
875  }
876 }
877 
878 
880 // find the pairs between our local particles and particles a1 ... (a2 - 1).
881 // if the last argument is true, initialize all the pairlists to be empty.
882 template < class T, unsigned Dim, class Mesh >
884  const unsigned a1, const unsigned a2, const bool initLists,
886 
887  unsigned i, j; // loop variables
888 
889  // initialize the pairlist storage if requested
890  if (initLists) {
891  unsigned vlen = PairList.size();
892  if (vlen > LocalNum)
893  vlen = LocalNum;
894  for (i=0; i < vlen; ++i)
895  PairList[i]->erase(PairList[i]->begin(), PairList[i]->end());
896 
897  // make sure there are enough single particle pairlists
898  if (PairList.size() < LocalNum) {
899  int newamt = LocalNum - PairList.size();
900  PairList.reserve(newamt);
901  for (int k=0; k < newamt; ++k)
902  PairList.push_back(new std::vector<pair_t>);
903  }
904  }
905 
906  // make sure we have something to do
907  if (a2 <= a1) return;
908 
909  // find pairs between local particles and particles a1 ... a2
910  for (i=0; i < LocalNum; ++i) {
911  // get interaction radius of this particle
912  T intrad1 = getInteractionRadius(i);
913 
914  // find starting index of inner loop
915  j = (a1 > i ? a1 : i + 1);
916 
917  // do inner loop for particles up to the last local one
918  // (these pairs must be stored twice)
919  for (; j < LocalNum; ++j) {
920  // add interaction radius of this particle
921  T intrad2 = intrad1 + getInteractionRadius(j);
922  intrad2 *= intrad2; // (intrad1 + intrad2)^2
923 
924  // find distance^2 between these two particles
925  Vektor<T,Dim> rsep = PData.R[j];
926  rsep -= PData.R[i];
927  T sep2 = dot(rsep, rsep);
928 
929  // if the separation is less than their interaction distance, book it
930  // we store the pair twice, since we know both i and j are < LocalNum
931  if (sep2 < intrad2) {
932  PairList[i]->push_back(pair_t(j, sep2));
933  PairList[j]->push_back(pair_t(i, sep2));
934  }
935  }
936 
937  // now do rest of loop for just ghost particles (only store the
938  // pair once in this case)
939  for (; j < a2; ++j) {
940  // get interaction radius of this particle
941  T intrad2 = intrad1 + getInteractionRadius(j);
942  intrad2 *= intrad2; // (intrad1 + intrad2)^2
943 
944  // find distance^2 between these two particles
945  Vektor<T,Dim> rsep = PData.R[j];
946  rsep -= PData.R[i];
947  T sep2 = dot(rsep, rsep);
948 
949  // if the separation is less than their interaction distance, book it
950  // we only store the pair for the local atom i once, since the other
951  // atom j is a ghost atom
952  if (sep2 < intrad2) {
953  PairList[i]->push_back(pair_t(j, sep2));
954  }
955  }
956  }
957 }
958 
959 
961 // print it out
962 template < class T, unsigned Dim, class Mesh >
963 std::ostream& operator<<(std::ostream& out, const ParticleInteractLayout<T,Dim,Mesh>& L) {
964 
965  out << "ParticleInteractLayout, with particle distribution:\n ";
966  for (unsigned int i=0; i < (unsigned int) Ippl::getNodes(); ++i)
967  out << L.getNodeCount(i) << " ";
968  out << "\nInteractLayout decomposition = " << L.getLayout();
969  return out;
970 }
971 
972 
974 // Repartition onto a new layout, if the layout changes ... this is a
975 // virtual function called by a UserList, as opposed to the RepartitionLayout
976 // function used by the particle load balancing mechanism.
977 template < class T, unsigned Dim, class Mesh >
979 
980  // perform actions to restructure our data due to a change in the
981  // RegionLayout
982  if (userlist->getUserListID() == this->RLayout.get_Id()) {
983  // clear out current interaction node storage; if the next update
984  // indicates we have a non-zero interaction radius, this info will be
985  // rebuilt (by calling rebuild_interaction_data)
986  InteractionNodes = 0;
987  setMaxInteractionRadius(0);
988  NeedGhostSwap = true;
989  }
990 }
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition: Vector3D.cpp:118
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 ParticleInteractLayout< 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 find_pairs(const unsigned LocalNum, const unsigned a1, const unsigned a2, const bool initLists, IpplParticleBase< ParticleInteractLayout< T, Dim, Mesh > > &PData)
virtual void Repartition(UserList *)
void update(IpplParticleBase< ParticleInteractLayout< T, Dim, Mesh > > &p, const ParticleAttrib< char > *canSwap=0)
void swap_ghost_particles(unsigned, IpplParticleBase< ParticleInteractLayout< T, Dim, Mesh > > &)
std::vector< pair_t >::iterator pair_iterator
void getPairlist(unsigned, pair_iterator &, pair_iterator &, IpplParticleBase< ParticleInteractLayout< T, Dim, Mesh > > &)
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