OPAL (Object Oriented Parallel Accelerator Library) 2022.1
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
33template < class T, unsigned Dim, class Mesh >
35 fl)
37 setup(); // perform necessary setup
38}
39
40
42// constructor, from a FieldLayout
43template < 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
53template < 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.
63template < class T, unsigned Dim, class Mesh >
66 setup(); // perform necessary setup
67}
68
69
71// perform common constructor tasks
72template < 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
90template < 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.
103template < 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.
124template < 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.
167template < 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
226template < 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();
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.
379template < 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.
519template < 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
654template < 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();
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) {
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.
882template < 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
962template < class T, unsigned Dim, class Mesh >
963std::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.
977template < 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}
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition: Vector3D.cpp:118
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
const unsigned Dim
#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
#define SWAP(a, b, type)
Definition: fftpack.cpp:83
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 PAssert(c)
Definition: PAssert.h:102
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
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