src/Particle/ParticleSpatialLayout.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /***************************************************************************
00003  *
00004  * The IPPL Framework
00005  * 
00006  *
00007  * Visit http://people.web.psi.ch/adelmann/ for more details
00008  *
00009  ***************************************************************************/
00010 
00011 #ifndef PARTICLE_SPATIAL_LAYOUT_H
00012 #define PARTICLE_SPATIAL_LAYOUT_H
00013 
00014 /*
00015  * ParticleSpatialLayout - particle layout based on spatial decomposition.
00016  *
00017  * This is a specialized version of ParticleLayout, which places particles
00018  * on processors based on their spatial location relative to a fixed grid.
00019  * In particular, this can maintain particles on processors based on a
00020  * specified FieldLayout or RegionLayout, so that particles are always on
00021  * the same node as the node containing the Field region to which they are
00022  * local.  This may also be used if there is no associated Field at all,
00023  * in which case a grid is selected based on an even distribution of
00024  * particles among processors.
00025  */
00026 
00027 // include files
00028 #include "Particle/ParticleLayout.h"
00029 #include "Particle/ParticleBase.h"
00030 #include "Region/RegionLayout.h"
00031 #include "Message/Message.h"
00032 #include "FieldLayout/FieldLayoutUser.h"
00033 #include <stddef.h>
00034 
00035 #ifdef IPPL_STDSTL
00036 #include <vector>
00037 using std::vector;
00038 #else
00039 #include <vector.h>
00040 #endif // IPPL_STDSTL
00041 
00042 #ifdef IPPL_USE_STANDARD_HEADERS
00043 #include <iostream>
00044 using namespace std;
00045 #else
00046 #include <iostream.h>
00047 #endif
00048 
00049 
00050 // forward declarations
00051 class UserList;
00052 template <class T> class ParticleAttrib;
00053 template <unsigned Dim, class T> class UniformCartesian;
00054 template <class T, unsigned Dim, class Mesh> class ParticleSpatialLayout;
00055 template <class T, unsigned Dim, class Mesh>
00056 ostream& operator<<(ostream&, const ParticleSpatialLayout<T,Dim,Mesh>&);
00057 
00058 
00059 // ParticleSpatialLayout class definition.  Template parameters are the type
00060 // and dimension of the ParticlePos object used for the particles.  The
00061 // dimension of the position must match the dimension of the FieldLayout
00062 // object used in this particle layout, if any.
00063 // Optional template parameter for the mesh type
00064 template < class T, unsigned Dim, class Mesh=UniformCartesian<Dim,T> >
00065 class ParticleSpatialLayout : public ParticleLayout<T, Dim>,
00066                               public FieldLayoutUser
00067 {
00068 
00069 public:
00070   // pair iterator definition ... this layout does not allow for pairlists
00071   typedef int pair_t;
00072   typedef pair_t* pair_iterator;
00073   typedef typename ParticleLayout<T, Dim>::SingleParticlePos_t 
00074     SingleParticlePos_t;
00075   typedef typename ParticleLayout<T, Dim>::Index_t Index_t;
00076 
00077   // type of attributes this layout should use for position and ID
00078   typedef ParticleAttrib<SingleParticlePos_t> ParticlePos_t;
00079   typedef ParticleAttrib<Index_t>             ParticleIndex_t;
00080 
00081 public:
00082   // constructor: The Field layout to which we match our particle's
00083   // locations.
00084   ParticleSpatialLayout(FieldLayout<Dim>&);
00085 
00086   // constructor: this one also takes a Mesh
00087   ParticleSpatialLayout(FieldLayout<Dim>&, Mesh&);
00088 
00089   // a similar constructor, but this one takes a RegionLayout.
00090   ParticleSpatialLayout(const RegionLayout<T,Dim,Mesh>&);
00091 
00092   // a default constructor ... in this case, no layout will
00093   // be assumed by this class.  A layout may be given later via the
00094   // 'setLayout' method, either as a FieldLayout or as a RegionLayout.
00095   ParticleSpatialLayout();
00096 
00097   // destructor
00098   ~ParticleSpatialLayout();
00099 
00100   //
00101   // spatial decomposition layout information
00102   //
00103 
00104   // retrieve a reference to the FieldLayout object in use.  This may be used,
00105   // e.g., to construct a Field with the same layout as the Particles.  Note
00106   // that if this object was constructed by providing a RegionLayout in the
00107   // constructor, then this generated FieldLayout will not necessarily match
00108   // up with the Region (it will be offset by some amount).  But, if this
00109   // object was either 1) created with a FieldLayout to begin with, or 2)
00110   // created with no layout, and one was generated internally, then the
00111   // returned FieldLayout will match and can be used to make new Fields or
00112   // Particles.
00113   FieldLayout<Dim>& getFieldLayout() { return RLayout.getFieldLayout(); }
00114 
00115   // retrieve a reference to the RegionLayout object in use
00116   RegionLayout<T,Dim,Mesh>& getLayout() { return RLayout; }
00117   const RegionLayout<T,Dim,Mesh>& getLayout() const { return RLayout; }
00118 
00119   // get number of particles on a physical node
00120   int getNodeCount(unsigned i) const {
00121     PAssert(i < Ippl::getNodes());
00122     return NodeCount[i];
00123   }
00124 
00125   // get flag for empty node domain
00126   bool getEmptyNode(unsigned i) const {
00127     PAssert(i < Ippl::getNodes());
00128     return EmptyNode[i];
00129   }
00130 
00131   //
00132   // Particle swapping/update routines
00133   //
00134 
00135   // Update the location and indices of all atoms in the given ParticleBase
00136   // object.  This handles swapping particles among processors if
00137   // needed, and handles create and destroy requests.  When complete,
00138   // all nodes have correct layout information.
00139   void update(ParticleBase< ParticleSpatialLayout<T,Dim,Mesh> >& p,
00140               const ParticleAttrib<char>* canSwap=0);
00141 
00142   //
00143   // I/O
00144   //
00145 
00146   // Print out information for debugging purposes.
00147   void printDebug(Inform&);
00148 
00149   //
00150   // virtual functions for FieldLayoutUser's (and other UserList users)
00151   //
00152 
00153   // Repartition onto a new layout
00154   virtual void Repartition(UserList *);
00155 
00156   // Tell this object that an object is being deleted
00157   virtual void notifyUserOfDelete(UserList *);
00158 
00159 protected:
00160   // The RegionLayout which determines where our particles go.
00161   RegionLayout<T,Dim,Mesh> RLayout;
00162 
00163   // The number of particles located on each physical node.
00164   size_t *NodeCount;
00165 
00166   // Flag for which nodes have no local domain
00167   bool* EmptyNode;
00168 
00169   // a list of Message pointers used in swapping particles, and flags
00170   // for which nodes expect messages in each dimension
00171   bool* SwapNodeList[Dim];
00172   Message** SwapMsgList;
00173   unsigned NeighborNodes[Dim];
00174   vector<size_t>* PutList;
00175 
00176   // perform common constructor tasks
00177   void setup();
00178 
00179   // for each dimension, calculate where neighboring Vnodes and physical
00180   // nodes are located, and create a list of this data.  This need only
00181   // be updated when the FieldLayout changes.
00182   void rebuild_neighbor_data();
00183 
00184   // recalculate our spatial decomposition.  PB is the type of ParticleBase
00185   // which should have it's layout rebuilt.
00186   //mwerks  template<class PB>
00187   //mwerks  void rebuild_layout(unsigned, PB&);
00189   // Rebuild the RegionLayout entirely, by recalculating our min and max
00190   // domains, adding a buffer region, and then giving this new Domain to
00191   // our internal RegionLayout.  When this is done, we must rebuild all
00192   // our other data structures as well.
00193   template < class PB >
00194   void rebuild_layout(size_t haveLocal, PB& PData) {
00195     TAU_TYPE_STRING(taustr, "void (unsigned, " + CT(PData) + " )"); 
00196     TAU_PROFILE("ParticleSpatialLayout::rebuild_layout()",taustr,TAU_PARTICLE);
00197 
00198     size_t i;
00199     unsigned d;                 // loop variables
00200 
00201     SingleParticlePos_t minpos = 0;
00202     SingleParticlePos_t maxpos = 0;
00203     int tag  = Ippl::Comm->next_tag(P_SPATIAL_RANGE_TAG, P_LAYOUT_CYCLE);
00204     int btag = Ippl::Comm->next_tag(P_SPATIAL_RANGE_TAG, P_LAYOUT_CYCLE);
00205 
00206     // if we have local particles, then find the min and max positions
00207     if (haveLocal > 0) {
00208       minpos = PData.R[0];
00209       maxpos = PData.R[0];
00210       for (i=1; i < haveLocal; ++i) {
00211         for (d=0; d < Dim; ++d) {
00212           if (PData.R[i][d] < minpos[d])
00213             minpos[d] = PData.R[i][d];
00214           if (PData.R[i][d] > maxpos[d])
00215             maxpos[d] = PData.R[i][d];
00216         }
00217       }
00218     }
00219 
00220     // if we're not on node 0, send data to node 0
00221     if (Ippl::myNode() != 0) {
00222       Message *msg = new Message;
00223       msg->put(haveLocal);
00224       if (haveLocal > 0) {
00225         minpos.putMessage(*msg);
00226         maxpos.putMessage(*msg);
00227       }
00228       Ippl::Comm->send(msg, 0, tag);
00229 
00230       // now receive back min and max range as provided by the master node.
00231       // These will include some buffer region, and will be integral values,
00232       // so we can make a FieldLayout and use it to initialize the RegionLayout.
00233       int node = 0;
00234       msg = Ippl::Comm->receive_block(node, btag);
00235       minpos.getMessage(*msg);
00236       maxpos.getMessage(*msg);
00237       delete msg;
00238 
00239     }
00240     else {                      // on node 0, collect data and compute region
00241       SingleParticlePos_t tmpminpos;
00242       SingleParticlePos_t tmpmaxpos;
00243       size_t tmphaveLocal;
00244       unsigned unreceived = Ippl::getNodes() - 1;
00245 
00246       // collect data from other nodes
00247       while (unreceived > 0) {
00248         int node = COMM_ANY_NODE;
00249         Message *msg = Ippl::Comm->receive_block(node, tag);
00250         msg->get(tmphaveLocal);
00251         if (tmphaveLocal > 0) {
00252           tmpminpos.getMessage(*msg);
00253           tmpmaxpos.getMessage(*msg);
00254           for (i=0; i < Dim; ++i) {
00255             if (tmpminpos[i] < minpos[i])
00256               minpos[i] = tmpminpos[i];
00257             if (tmpmaxpos[i] > maxpos[i])
00258               maxpos[i] = tmpmaxpos[i];
00259           }
00260         }
00261         delete msg;
00262         unreceived--;
00263       }
00264 
00265       // adjust min and max to include a buffer region and fall on integral
00266       // values
00267       SingleParticlePos_t extrapos = (maxpos - minpos) * ((T)0.125);
00268       maxpos += extrapos;
00269       minpos -= extrapos;
00270       for (i=0; i < Dim; ++i) {
00271         if (minpos[i] >= 0.0)
00272           minpos[i] = (int)(minpos[i]);
00273         else
00274           minpos[i] = (int)(minpos[i] - 1);
00275         maxpos[i] = (int)(maxpos[i] + 1);
00276       }
00277 
00278       // send these values out to the other nodes
00279       if (Ippl::getNodes() > 1) {
00280         Message *bmsg = new Message;
00281         minpos.putMessage(*bmsg);
00282         maxpos.putMessage(*bmsg);
00283         Ippl::Comm->broadcast_others(bmsg, btag);
00284       }
00285     }
00286 
00287     // determine the size of the new domain, and the number of blocks into
00288     // which it should be broken
00289     NDIndex<Dim> range;
00290     for (i=0; i < Dim; ++i)
00291       range[i] = Index((int)(minpos[i]), (int)(maxpos[i]));
00292     int vn = -1;
00293     if (RLayout.initialized())
00294       vn = RLayout.size_iv() + RLayout.size_rdv();
00295 
00296     // ask the RegionLayout to change the paritioning to match this size
00297     // and block count.  This will eventually end up by calling Repartition
00298     // here, which will lead to rebuilding the neighbor data, etc., so we
00299     // are done.
00300     RLayout.changeDomain(range, vn);
00301   }
00302 
00303   // swap particles to neighboring nodes if they have moved too far
00304   // PB is the type of ParticleBase which should have it's layout rebuilt.
00305   //mwerks  template<class PB>
00306   //mwerks  unsigned swap_particles(unsigned, PB&);
00308   // go through all our local particles, and send particles which must
00309   // be swapped to another node to that node.
00310   template < class PB >
00311   size_t swap_particles(size_t LocalNum, PB& PData) {
00312 
00313     TAU_TYPE_STRING(taustr, "unsigned (unsigned, " + CT(PData) + " )");
00314     TAU_PROFILE("ParticleSpatialLayout::swap_particles()", taustr, 
00315                 TAU_PARTICLE);
00316 
00317     Inform msg("ParticleSpatialLayout ERROR ", INFORM_ALL_NODES);
00318     
00319     unsigned d, i, j;                   // loop variables
00320     size_t ip;
00321     unsigned N = Ippl::getNodes();
00322     unsigned myN = Ippl::myNode();
00323 
00324   // iterators used to search local domains
00325     typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
00326 
00327   // iterators used to search remote domains
00328     typename RegionLayout<T,Dim,Mesh>::iterator_dv remoteV, remoteEnd = RLayout.end_rdv();
00329 
00330     // JCC: This "nudge factor" stuff was added when we were experiencing
00331     // problems with particles getting lost in between PRegions on 
00332     // neighboring nodes.  This problem has since been resolved by 
00333     // fixing the way in which PRegion boundaries are computed, so I am
00334     // commenting this out for now.  We can bring it back later if the 
00335     // need arises.
00336 
00337     /*
00338 
00339     // Calculate a 'nudge factor', an amount that can get added to a
00340     // particle position to determine where it should be located.  The nudge
00341     // factor equals 1/100th the smallest width of the rnodes in each dimension.
00342     // When we try to find where a particle is located, we check what vnode
00343     // contains this particle 'nudge region', a box around the particle's pos
00344     // of the size of the nudge factor.
00345     T pNudge[Dim];
00346     for (d=0; d < Dim; ++d) {
00347       // initialize to the first rnode's width
00348       T minval = (*(RLayout.begin_iv())).second->getDomain()[d].length();
00349 
00350       // check the local rnodes
00351       for (localV = RLayout.begin_iv(); localV != localEnd; ++localV) {
00352         T checkval = (*localV).second->getDomain()[d].length();
00353         if (checkval < minval)
00354           minval = checkval;
00355       }
00356 
00357       // check the remote rnodes
00358       for (remoteV = RLayout.begin_rdv(); remoteV != remoteEnd; ++remoteV) {
00359         T checkval = (*remoteV).second->getDomain()[d].length();
00360         if (checkval < minval)
00361           minval = checkval;
00362       }
00363 
00364       // now rescale the minval, and save it
00365       pNudge[d] = 0.00001 * minval;
00366     }
00367 
00368     */
00369 
00370     // An NDRegion object used to store a particle position.
00371     NDRegion<T,Dim> pLoc;
00372 
00373     // get new message tag for particle exchange with empty domains
00374     int etag = Ippl::Comm->next_tag(P_SPATIAL_RETURN_TAG,P_LAYOUT_CYCLE);
00375 
00376     if (!getEmptyNode(myN)) {
00377 
00378       // Particles are swapped in multipple passes, one for each dimension.
00379       // The tasks completed here for each dimension are the following:
00380       //   1. For each local Vnode, find the remote Vnodes which exist along
00381       //      same axis as the current axis (i.e. all Vnodes along the x-axis).
00382       //   2. From this list, determine which nodes we send messages to.
00383       //      Steps 1 & 2 have been done already in rebuild_neighbor_data.
00384       //   3. Go through all the particles, finding those which have moved to
00385       //      an off-processor vnode, and store index in an array for that node
00386       //   4. Send off the particles to the nodes (if no particles are
00387       //      going to a node, send them a message with 0 in it)
00388       //   5. Delete the send particles from our local list
00389       //   6. Receive particles sent to us by other nodes (some messages may
00390       //      say that we're receiving 0 particles from that node).
00391 
00392       // Initialize NDRegion with a position inside the first Vnode.
00393       // We can skip dim 0, since it will be filled below.
00394       for (d = 1; d < Dim; ++d) {
00395         T first = (*(RLayout.begin_iv())).second->getDomain()[d].first();
00396         T last  = (*(RLayout.begin_iv())).second->getDomain()[d].last();
00397         T mid   = first + 0.5 * (last - first);
00398         pLoc[d] = PRegion<T>(mid, mid);
00399       }
00400 
00401       for (d = 0; d < Dim; ++d) {
00402 
00403         // get new message tag for particle exchange along this dimension
00404         int tag = Ippl::Comm->next_tag(P_SPATIAL_TRANSFER_TAG,P_LAYOUT_CYCLE);
00405 
00406         // we only need to do the rest if there are other nodes in this dim
00407         if (NeighborNodes[d] > 0) {
00408           // create new messages to send to our neighbors
00409           for (i = 0; i < N; i++)
00410             if (SwapNodeList[d][i])
00411               SwapMsgList[i] = new Message;
00412 
00413           // Go through the particles and find those moving in the current dir.
00414           // When one is found, copy it into outgoing message and delete it.
00415           for (ip=0; ip<LocalNum; ++ip) {
00416             // get the position of particle ip, and find the closest grid pnt
00417             // for just the dimensions 0 ... d
00418             for (j = 0; j <= d; j++)
00419               pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
00420 
00421             // first check local domains (in this dimension)
00422             bool foundit = false;
00423             // JCC:       int nudged = 0;
00424             while (!foundit) {
00425               for (localV = RLayout.begin_iv();
00426                    localV != localEnd && !foundit; ++localV) {
00427                 foundit= (((*localV).second)->getDomain())[d].touches(pLoc[d]);
00428               }
00429 
00430               // if not found, it might be remote
00431               if (!foundit) {
00432                 // see which Vnode this postion is in
00433                 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
00434                   RLayout.touch_range_rdv(pLoc);
00435 
00436                 // make sure we have a vnode to send it to
00437                 if (touchingVN.first == touchingVN.second) {
00438                   // JCC:               if (nudged >= Dim) {
00439                   ERRORMSG("Local particle " << ip << " with ID=");
00440                   ERRORMSG(PData.ID[ip] << " at ");
00441                   ERRORMSG(PData.R[ip] << " is outside of global domain ");
00442                   ERRORMSG(RLayout.getDomain() << endl);
00443                   ERRORMSG("This occurred when searching for point " << pLoc);
00444                   ERRORMSG(" in RegionLayout = " << RLayout << endl);
00445                   Ippl::abort();
00446 
00447                   // JCC:
00448                   /*
00449                   }
00450                   else {
00451                     DEBUGMSG("Local particle " << ip << " with ID=");
00452                     DEBUGMSG(PData.ID[ip] << " at ");
00453                     DEBUGMSG(PData.R[ip] << " might be outside of global domain ");
00454                     DEBUGMSG(RLayout.getDomain() << endl);
00455                     DEBUGMSG("Attempting to nudge it to the right to see if it ");
00456                     DEBUGMSG("is in a crack, along dim = " << nudged << endl);
00457                     DEBUGMSG("Previously checked  pos = " << pLoc << endl);
00458                     T oldval = PData.R[ip][nudged];
00459                     pLoc[nudged] = PRegion<T>(oldval, oldval + pNudge[nudged]);
00460                     nudged++;
00461                     DEBUGMSG("Will check with new pos = " << pLoc << endl);
00462                   }
00463                   */
00464 
00465                 }
00466                 else {
00467                   // the node has been found - add index to put list
00468                   unsigned node = (*(touchingVN.first)).second->getNode();
00469                   PAssert(SwapNodeList[d][node]);
00470                   PutList[node].push_back(ip);
00471 
00472                   // .. and then add to DestroyList
00473                   PData.destroy(1, ip);
00474 
00475                   // indicate we found it to quit this check
00476                   foundit = true;
00477                 }
00478               }
00479             }
00480           }
00481 
00482           // send the particles to their destination nodes
00483           for (i = 0; i < N; i++) {
00484             if (SwapNodeList[d][i]) {
00485               // put data for particles on this put list into message
00486               PData.putMessage( *(SwapMsgList[i]), PutList[i] );
00487 
00488               // add a final 'zero' number of particles to indicate the end
00489               PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
00490 
00491               // send the message
00492               // Inform dbgmsg("SpatialLayout", INFORM_ALL_NODES);
00493               //dbgmsg << "Swapping "<<PutList[i].size() << " particles to node ";
00494               //dbgmsg << i<<" with tag " << tag << " (" << 'x' + d << ")" << endl;
00495               //dbgmsg << "  ... msg = " << *(SwapMsgList[i]) << endl;
00496               int node = i;
00497               Ippl::Comm->send(SwapMsgList[i], node, tag);
00498 
00499               // clear the list
00500               PutList[i].erase(PutList[i].begin(), PutList[i].end());
00501             }
00502           }
00503 
00504           LocalNum -= PData.getDestroyNum();  // update local num
00505           ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
00506           PData.performDestroy();
00507 
00508           // receive particles from neighbor nodes, and add them to our list
00509           unsigned sendnum = NeighborNodes[d];
00510           while (sendnum-- > 0) {
00511             int node = Communicate::COMM_ANY_NODE;
00512             Message *recmsg = Ippl::Comm->receive_block(node, tag);
00513             size_t recvd;
00514             while ((recvd = PData.getMessage(*recmsg)) > 0)
00515               LocalNum += recvd;
00516             delete recmsg;
00517           }
00518         }  // end if (NeighborNodes[d] > 0)
00519 
00520         if (d == 0) {
00521           // receive messages from any empty nodes
00522           for (i = 0; i < N; ++i) {
00523             if (getEmptyNode(i)) {
00524               int node = i;
00525               Message *recmsg = Ippl::Comm->receive_block(node, etag);
00526               size_t recvd;
00527               while ((recvd = PData.getMessage(*recmsg)) > 0)
00528                 LocalNum += recvd;
00529               delete recmsg;
00530             }
00531           }
00532         }
00533 
00534       }  // end for (d=0; d<Dim; ++d)
00535 
00536     }
00537     else { // empty node sends, but does not receive
00538       msg << "case getEmptyNode(myN) " << endl;
00539       // create new messages to send to our neighbors along dim 0
00540       for (i = 0; i < N; i++)
00541         if (SwapNodeList[0][i])
00542           SwapMsgList[i] = new Message;
00543 
00544       // Go through the particles and find those moving to other nodes.
00545       // When one is found, copy it into outgoing message and delete it.
00546       for (ip=0; ip<LocalNum; ++ip) {
00547         // get the position of particle ip, and find the closest grid pnt
00548         for (j = 0; j < Dim; j++)
00549           pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
00550 
00551         // see which remote Vnode this postion is in
00552         typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
00553           RLayout.touch_range_rdv(pLoc);
00554 
00555         // make sure we have a vnode to send it to
00556         if (touchingVN.first == touchingVN.second) {
00557           ERRORMSG("Local particle " << ip << " with ID=");
00558           ERRORMSG(PData.ID[ip] << " at ");
00559           ERRORMSG(PData.R[ip] << " is outside of global domain ");
00560           ERRORMSG(RLayout.getDomain() << endl);
00561           ERRORMSG("This occurred when searching for point " << pLoc);
00562           ERRORMSG(" in RegionLayout = " << RLayout << endl);
00563           Ippl::abort();
00564         }
00565         else {
00566           // the node has been found - add index to put list
00567           unsigned node = (*(touchingVN.first)).second->getNode();
00568           PAssert(SwapNodeList[0][node]);
00569           PutList[node].push_back(ip);
00570 
00571           // .. and then add to DestroyList
00572           PData.destroy(1, ip);
00573         }
00574       }
00575 
00576       // send the particles to their destination nodes
00577       for (i = 0; i < N; i++) {
00578         if (SwapNodeList[0][i]) {
00579           // put data for particles on this put list into message
00580           PData.putMessage( *(SwapMsgList[i]), PutList[i] );
00581 
00582           // add a final 'zero' number of particles to indicate the end
00583           PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
00584 
00585           // send the message
00586           int node = i;
00587           Ippl::Comm->send(SwapMsgList[i], node, etag);
00588 
00589           // clear the list
00590           PutList[i].erase(PutList[i].begin(), PutList[i].end());
00591         }
00592       }
00593 
00594       LocalNum -= PData.getDestroyNum();  // update local num
00595       ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
00596       PData.performDestroy();
00597 
00598     }
00599 
00600     // return how many particles we have now
00601     return LocalNum;
00602   }
00603 
00604   // PB is the type of ParticleBase which should have it's layout rebuilt.
00605   //mwerks  template<class PB>
00606   //mwerks  unsigned swap_particles(unsigned, PB&, const ParticleAttrib<char>&);
00608   // go through all our local particles, and send particles which must
00609   // be swapped to another node to that node.
00610   template < class PB >
00611   size_t swap_particles(size_t LocalNum, PB& PData,
00612                         const ParticleAttrib<char>& canSwap) {
00613 
00614     TAU_TYPE_STRING(taustr, "unsigned (unsigned, " + CT(PData) + ", const ParticleAttrib<char>&)"); 
00615     TAU_PROFILE("ParticleSpatialLayout::swap_particles()", taustr, 
00616                 TAU_PARTICLE);
00617 
00618     unsigned d, i, j;                   // loop variables
00619     size_t ip;
00620     unsigned N = Ippl::getNodes();
00621     unsigned myN = Ippl::myNode();
00622 
00623   // iterators used to search local domains
00624     typename RegionLayout<T,Dim,Mesh>::iterator_iv localV, localEnd = RLayout.end_iv();
00625 
00626   // iterators used to search remote domains
00627     typename RegionLayout<T,Dim,Mesh>::iterator_dv remoteV, remoteEnd = RLayout.end_rdv();
00628 
00629     // JCC: This "nudge factor" stuff was added when we were experiencing
00630     // problems with particles getting lost in between PRegions on 
00631     // neighboring nodes.  This problem has since been resolved by 
00632     // fixing the way in which PRegion boundaries are computed, so I am
00633     // commenting this out for now.  We can bring it back later if the 
00634     // need arises.
00635 
00636     /*
00637 
00638     // Calculate a 'nudge factor', an amount that can get added to a
00639     // particle position to determine where it should be located.  The nudge
00640     // factor equals 1/100th the smallest width of the rnodes in each dimension.
00641     // When we try to find where a particle is located, we check what vnode
00642     // contains this particle 'nudge region', a box around the particle's pos
00643     // of the size of the nudge factor.
00644     T pNudge[Dim];
00645     for (d=0; d < Dim; ++d) {
00646       // initialize to the first rnode's width
00647       T minval = (*(RLayout.begin_iv())).second->getDomain()[d].length();
00648 
00649       // check the local rnodes
00650       for (localV = RLayout.begin_iv(); localV != localEnd; ++localV) {
00651         T checkval = (*localV).second->getDomain()[d].length();
00652         if (checkval < minval)
00653           minval = checkval;
00654       }
00655 
00656       // check the remote rnodes
00657       for (remoteV = RLayout.begin_rdv(); remoteV != remoteEnd; ++remoteV) {
00658         T checkval = (*remoteV).second->getDomain()[d].length();
00659         if (checkval < minval)
00660           minval = checkval;
00661       }
00662 
00663       // now rescale the minval, and save it
00664       pNudge[d] = 0.00001 * minval;
00665     }
00666 
00667     */
00668 
00669     // An NDRegion object used to store a particle position.
00670     NDRegion<T,Dim> pLoc;
00671 
00672     // get new message tag for particle exchange with empty domains
00673     int etag = Ippl::Comm->next_tag(P_SPATIAL_RETURN_TAG,P_LAYOUT_CYCLE);
00674 
00675     if (!getEmptyNode(myN)) {
00676 
00677       // Particles are swapped in multipple passes, one for each dimension.
00678       // The tasks completed here for each dimension are the following:
00679       //   1. For each local Vnode, find the remote Vnodes which exist along
00680       //      same axis as the current axis (i.e. all Vnodes along the x-axis).
00681       //   2. From this list, determine which nodes we send messages to.
00682       //      Steps 1 & 2 have been done already in rebuild_neighbor_data.
00683       //   3. Go through all the particles, finding those which have moved to
00684       //      an off-processor vnode, and store index in an array for that node
00685       //   4. Send off the particles to the nodes (if no particles are
00686       //      going to a node, send them a message with 0 in it)
00687       //   5. Delete the send particles from our local list
00688       //   6. Receive particles sent to us by other nodes (some messages may
00689       //      say that we're receiving 0 particles from that node).
00690 
00691       // Initialize NDRegion with a position inside the first Vnode.
00692       // We can skip dim 0, since it will be filled below.
00693       for (d = 1; d < Dim; ++d) {
00694         T first = (*(RLayout.begin_iv())).second->getDomain()[d].first();
00695         T last  = (*(RLayout.begin_iv())).second->getDomain()[d].last();
00696         T mid   = first + 0.5 * (last - first);
00697         pLoc[d] = PRegion<T>(mid, mid);
00698       }
00699 
00700       for (d = 0; d < Dim; ++d) {
00701 
00702         // get new message tag for particle exchange along this dimension
00703         int tag = Ippl::Comm->next_tag(P_SPATIAL_TRANSFER_TAG,P_LAYOUT_CYCLE);
00704 
00705         // we only need to do the rest if there are other nodes in this dim
00706         if (NeighborNodes[d] > 0) {
00707           // create new messages to send to our neighbors
00708           for (i = 0; i < N; i++)
00709             if (SwapNodeList[d][i])
00710               SwapMsgList[i] = new Message;
00711 
00712           // Go through the particles and find those moving in the current dir.
00713           // When one is found, copy it into outgoing message and delete it.
00714           for (ip=0; ip<LocalNum; ++ip) {
00715             if (!bool(canSwap[ip])) continue;  // skip if can't swap
00716             // get the position of particle ip, and find the closest grid pnt
00717             // for just the dimensions 0 ... d
00718             for (j = 0; j <= d; j++)
00719               pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
00720 
00721             // first check local domains (in this dimension)
00722             bool foundit = false;
00723             // JCC:       int nudged = 0;
00724             while (!foundit) {
00725               for (localV = RLayout.begin_iv();
00726                    localV != localEnd && !foundit; ++localV) {
00727                 foundit= (((*localV).second)->getDomain())[d].touches(pLoc[d]);
00728               }
00729 
00730               // if not found, it might be remote
00731               if (!foundit) {
00732                 // see which Vnode this postion is in
00733                 typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
00734                   RLayout.touch_range_rdv(pLoc);
00735 
00736                 // make sure we have a vnode to send it to
00737                 if (touchingVN.first == touchingVN.second) {
00738                   // JCC:               if (nudged >= Dim) {
00739                   ERRORMSG("Local particle " << ip << " with ID=");
00740                   ERRORMSG(PData.ID[ip] << " at ");
00741                   ERRORMSG(PData.R[ip] << " is outside of global domain ");
00742                   ERRORMSG(RLayout.getDomain() << endl);
00743                   ERRORMSG("This occurred when searching for point " << pLoc);
00744                   ERRORMSG(" in RegionLayout = " << RLayout << endl);
00745                   Ippl::abort();
00746 
00747                   // JCC:
00748                   /*
00749                   }
00750                   else {
00751                     DEBUGMSG("Local particle " << ip << " with ID=");
00752                     DEBUGMSG(PData.ID[ip] << " at ");
00753                     DEBUGMSG(PData.R[ip] << " might be outside of global domain ");
00754                     DEBUGMSG(RLayout.getDomain() << endl);
00755                     DEBUGMSG("Attempting to nudge it to the right to see if it ");
00756                     DEBUGMSG("is in a crack, along dim = " << nudged << endl);
00757                     DEBUGMSG("Previously checked  pos = " << pLoc << endl);
00758                     T oldval = PData.R[ip][nudged];
00759                     pLoc[nudged] = PRegion<T>(oldval, oldval + pNudge[nudged]);
00760                     nudged++;
00761                     DEBUGMSG("Will check with new pos = " << pLoc << endl);
00762                   }
00763                   */
00764 
00765                 }
00766                 else {
00767                   // the node has been found - add index to put list
00768                   unsigned node = (*(touchingVN.first)).second->getNode();
00769                   PAssert(SwapNodeList[d][node]);
00770                   PutList[node].push_back(ip);
00771 
00772                   // .. and then add to DestroyList
00773                   PData.destroy(1, ip);
00774 
00775                   // indicate we found it to quit this check
00776                   foundit = true;
00777                 }
00778               }
00779             }
00780           }
00781 
00782           // send the particles to their destination nodes
00783           for (i = 0; i < N; i++) {
00784             if (SwapNodeList[d][i]) {
00785               // put data for particles on this put list into message
00786               PData.putMessage( *(SwapMsgList[i]), PutList[i] );
00787 
00788               // add a final 'zero' number of particles to indicate the end
00789               PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
00790 
00791               // send the message
00792               //Inform dbgmsg("SpatialLayout", INFORM_ALL_NODES);
00793               //dbgmsg << "Swapping "<<PutList[i].size() << " particles to node ";
00794               //dbgmsg << i<<" with tag " << tag << " (" << 'x' + d << ")" << endl;
00795               //dbgmsg << "  ... msg = " << *(SwapMsgList[i]) << endl;
00796               int node = i;
00797               Ippl::Comm->send(SwapMsgList[i], node, tag);
00798 
00799               // clear the list
00800               PutList[i].erase(PutList[i].begin(), PutList[i].end());
00801             }
00802           }
00803 
00804           LocalNum -= PData.getDestroyNum();  // update local num
00805           ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
00806           PData.performDestroy();
00807 
00808           // receive particles from neighbor nodes, and add them to our list
00809           unsigned sendnum = NeighborNodes[d];
00810           while (sendnum-- > 0) {
00811             int node = Communicate::COMM_ANY_NODE;
00812             Message *recmsg = Ippl::Comm->receive_block(node, tag);
00813             size_t recvd;
00814             while ((recvd = PData.getMessage(*recmsg)) > 0)
00815               LocalNum += recvd;
00816             delete recmsg;
00817           }
00818         }  // end if (NeighborNodes[d] > 0)
00819 
00820         if (d == 0) {
00821           // receive messages from any empty nodes
00822           for (i = 0; i < N; ++i) {
00823             if (getEmptyNode(i)) {
00824               int node = i;
00825               Message *recmsg = Ippl::Comm->receive_block(node, etag);
00826               size_t recvd;
00827               while ((recvd = PData.getMessage(*recmsg)) > 0)
00828                 LocalNum += recvd;
00829               delete recmsg;
00830             }
00831           }
00832         }
00833 
00834       }  // end for (d=0; d<Dim; ++d)
00835 
00836     }
00837     else { // empty node sends, but does not receive
00838       // create new messages to send to our neighbors along dim 0
00839       for (i = 0; i < N; i++)
00840         if (SwapNodeList[0][i])
00841           SwapMsgList[i] = new Message;
00842 
00843       // Go through the particles and find those moving to other nodes.
00844       // When one is found, copy it into outgoing message and delete it.
00845       for (ip=0; ip<LocalNum; ++ip) {
00846         if (!bool(canSwap[ip])) continue;  // skip if can't swap
00847         // get the position of particle ip, and find the closest grid pnt
00848         for (j = 0; j < Dim; j++)
00849           pLoc[j] = PRegion<T>(PData.R[ip][j], PData.R[ip][j]);
00850 
00851         // see which remote Vnode this postion is in
00852         typename RegionLayout<T,Dim,Mesh>::touch_range_dv touchingVN =
00853           RLayout.touch_range_rdv(pLoc);
00854 
00855         // make sure we have a vnode to send it to
00856         if (touchingVN.first == touchingVN.second) {
00857           ERRORMSG("Local particle " << ip << " with ID=");
00858           ERRORMSG(PData.ID[ip] << " at ");
00859           ERRORMSG(PData.R[ip] << " is outside of global domain ");
00860           ERRORMSG(RLayout.getDomain() << endl);
00861           ERRORMSG("This occurred when searching for point " << pLoc);
00862           ERRORMSG(" in RegionLayout = " << RLayout << endl);
00863           Ippl::abort();
00864         }
00865         else {
00866           // the node has been found - add index to put list
00867           unsigned node = (*(touchingVN.first)).second->getNode();
00868           PAssert(SwapNodeList[0][node]);
00869           PutList[node].push_back(ip);
00870 
00871           // .. and then add to DestroyList
00872           PData.destroy(1, ip);
00873         }
00874       }
00875 
00876       // send the particles to their destination nodes
00877       for (i = 0; i < N; i++) {
00878         if (SwapNodeList[0][i]) {
00879           // put data for particles on this put list into message
00880           PData.putMessage( *(SwapMsgList[i]), PutList[i] );
00881 
00882           // add a final 'zero' number of particles to indicate the end
00883           PData.putMessage(*(SwapMsgList[i]), (size_t) 0, (size_t) 0);
00884 
00885           // send the message
00886           int node = i;
00887           Ippl::Comm->send(SwapMsgList[i], node, etag);
00888 
00889           // clear the list
00890           PutList[i].erase(PutList[i].begin(), PutList[i].end());
00891         }
00892       }
00893 
00894       LocalNum -= PData.getDestroyNum();  // update local num
00895       ADDIPPLSTAT(incParticlesSwapped, PData.getDestroyNum());
00896       PData.performDestroy();
00897 
00898     }
00899 
00900     // return how many particles we have now
00901     return LocalNum;
00902   }
00903 };
00904 
00905 #include "Particle/ParticleSpatialLayout.cpp"
00906 
00907 #endif // PARTICLE_SPATIAL_LAYOUT_H
00908 
00909 /***************************************************************************
00910  * $RCSfile: ParticleSpatialLayout.h,v $   $Author: adelmann $
00911  * $Revision: 1.1.1.1 $   $Date: 2003/01/23 07:40:29 $
00912  * IPPL_VERSION_ID: $Id: ParticleSpatialLayout.h,v 1.1.1.1 2003/01/23 07:40:29 adelmann Exp $ 
00913  ***************************************************************************/

Generated on Mon Jan 16 13:23:53 2006 for IPPL by  doxygen 1.4.6