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_BASE_H 00012 #define PARTICLE_BASE_H 00013 00014 /* 00015 * ParticleBase - Base class for all user-defined particle classes. 00016 * 00017 * ParticleBase is a container and manager for a set of particles. 00018 * The user must define a class derived from ParticleBase which describes 00019 * what specific data attributes the particle has (e.g., mass or charge). 00020 * Each attribute is an instance of a ParticleAttribute<T> class; ParticleBase 00021 * keeps a list of pointers to these attributes, and performs global 00022 * operations on them such as update, particle creation and destruction, 00023 * and inter-processor particle migration. 00024 * 00025 * ParticleBase is templated on the ParticleLayout mechanism for the particles. 00026 * This template parameter should be a class derived from ParticleLayout. 00027 * ParticleLayout-derived classes maintain the info on which particles are 00028 * located on which processor, and performs the specific communication 00029 * required between processors for the particles. The ParticleLayout is 00030 * templated on the type and dimension of the atom position attribute, and 00031 * ParticleBase uses the same types for these items as the given 00032 * ParticleLayout. 00033 * 00034 * ParticleBase and all derived classes have the following common 00035 * characteristics: 00036 * - The spatial positions of the N particles are stored in the 00037 * ParticlePos_t variable R 00038 * - The global index of the N particles are stored in the 00039 * ParticleIndex_t variable ID 00040 * - A pointer to an allocated layout class. When you construct a 00041 * ParticleBase, you must provide a layout instance, and ParticleBase 00042 * will delete this instance when it (the ParticleBase) is deleted. 00043 * 00044 * To use this class, the user defines a derived class with the same 00045 * structure as in this example: 00046 * 00047 * class UserParticles : 00048 * public ParticleBase< ParticleSpatialLayout<double,2> > { 00049 * public: 00050 * // attributes for this class 00051 * ParticleAttribute<double> rad; // radius 00052 * ParticlePos_t vel; // velocity, same storage type as R 00053 * 00054 * // constructor: add attributes to base class 00055 * UserParticles(ParticleSpatialLayout<double,2>* L) : ParticleBase(L) { 00056 * addAttribute(rad); 00057 * addAttribute(vel); 00058 * } 00059 * }; 00060 * 00061 * This example defines a user class with 2D position and two extra 00062 * attributes: a radius rad (double), and a velocity vel (a 2D Vektor). 00063 * 00064 * After each 'time step' in a calculation, which is defined as a period 00065 * in which the particle positions may change enough to affect the global 00066 * layout, the user must call the 'update' routine, which will move 00067 * particles between processors, etc. After the Nth call to update, a 00068 * load balancing routine will be called instead. The user may set the 00069 * frequency of load balancing (N), or may supply a function to 00070 * determine if load balancing should be done or not. 00071 * 00072 * Each ParticleBase can contain zero or more 'ghost' particles, which are 00073 * copies of particle data collected from other nodes. These ghost particles 00074 * have many of the same function calls as for the 'regular' particles, with 00075 * the word 'ghost' prepended. They are not necessary; but may be used to 00076 * improve performance of some parallel algorithms (such as calculating 00077 * neighbor lists). The actual determination of what ghost particles should 00078 * be stored in this object (if any) is done by the specific layout object. 00079 * 00080 * ParticleBase also contains information on the types of boundary conditions 00081 * to use. ParticleBase contains a ParticleBConds object, which is an array 00082 * of particle boundary condition functions. By default, these BC's are null, 00083 * so that nothing special happens at the boundary, but the user may set these 00084 * BC's by using the 'getBConds' method to access the BC container. The BC's 00085 * will then be used at the next update. In fact, the BC container is stored 00086 * within the ParticleLayout object, but the interface the user uses to access 00087 * it is via ParticleBase. 00088 * 00089 * You can create an uninitialized ParticleBase, by using the default 00090 * constructor. In this case, it will not do any initialization of data 00091 * structures, etc., and will not contain a layout instance. In order to use 00092 * the ParticleBase in any useful way in this case, use the 'initialize()' 00093 * method which takes as an argument the layout instance to use. 00094 */ 00095 00096 // include files 00097 #include "Particle/ParticleAttrib.h" 00098 #include "AppTypes/Vektor.h" 00099 #include "DataSource/DataSource.h" 00100 #include "DataSource/MakeDataSource.h" 00101 00102 #ifdef IPPL_STDSTL 00103 #include <vector> 00104 #include <algorithm> // Include algorithms 00105 #include <utility> 00106 using std::vector; 00107 using std::sort; 00108 using std::pair; 00109 #else 00110 #include <vector.h> 00111 #include <pair.h> 00112 #endif // IPPL_STDSTL 00113 00114 #ifdef IPPL_USE_STANDARD_HEADERS 00115 #include <iostream> 00116 using namespace std; 00117 #else 00118 #include <iostream.h> 00119 #endif 00120 00121 00122 // forward declarations 00123 class Inform; 00124 class Message; 00125 template <class PLayout> class ParticleBase; 00126 template <class PLayout> 00127 ostream& operator<<(ostream&, const ParticleBase<PLayout>&); 00128 template <class T, unsigned D> class ParticleBConds; 00129 00130 00131 // ParticleBase class definition. Template parameter is the specific 00132 // ParticleLayout-derived class which determines how the particles are 00133 // distributed among processors. 00134 template<class PLayout> 00135 class ParticleBase : public DataSource { 00136 00137 public: 00138 // useful enums 00139 enum { Dim = PLayout::Dimension }; 00140 00141 // useful typedefs and enums 00142 typedef PLayout Layout_t; 00143 typedef typename PLayout::Position_t Position_t; 00144 typedef typename PLayout::Index_t Index_t; 00145 00146 typedef typename PLayout::ParticlePos_t ParticlePos_t; 00147 typedef typename PLayout::ParticleIndex_t ParticleIndex_t; 00148 00149 typedef typename PLayout::pair_iterator pair_iterator; 00150 typedef typename PLayout::pair_t pair_t; 00151 typedef typename PLayout::UpdateFlags UpdateFlags; 00152 typedef vector<ParticleAttribBase *> attrib_container_t; 00153 typedef attrib_container_t::iterator attrib_iterator; 00154 typedef ParticleAttribBase::SortList_t SortList_t; 00155 00156 // our position, and our global ID's 00157 ParticlePos_t R; 00158 ParticleIndex_t ID; 00159 00160 public: 00161 // constructor 1: no arguments, so create an uninitialized ParticleBase. 00162 // If this constructor is used, the user must call 'initialize' with 00163 // a layout object in order to use this. 00164 ParticleBase() : Layout(0) { } 00165 00166 // constructor 2: arguments = layout to use. 00167 ParticleBase(PLayout *layout) : Layout(layout) { setup(); } 00168 00169 // destructor - delete the layout if necessary 00170 ~ParticleBase() { 00171 if (Layout != 0) 00172 delete Layout; 00173 } 00174 00175 // 00176 // Initialization methods 00177 // 00178 00179 // For a ParticleBase that was created with the default constructor, 00180 // initialize performs the same actions as are done in the non-default 00181 // constructor. If this object has already been initialized, it is 00182 // an error. For initialize, you must supply a layout instance. 00183 void initialize(PLayout *); 00184 00185 00186 // 00187 // Accessor functions for this class 00188 // 00189 00190 // return/change the total or local number of particles 00191 size_t getTotalNum() const { return TotalNum; } 00192 size_t getLocalNum() const { return LocalNum; } 00193 size_t getDestroyNum() const { return DestroyNum; } 00194 size_t getGhostNum() const { return GhostNum; } 00195 void setTotalNum(size_t n) { TotalNum = n; } 00196 void setLocalNum(size_t n) { LocalNum = n; } 00197 00198 // get the layout manager 00199 PLayout& getLayout() { return *Layout; } 00200 const PLayout& getLayout() const { return *Layout; } 00201 00202 // get or set the boundary conditions container 00203 ParticleBConds<Position_t,PLayout::Dimension>& getBConds() { 00204 return Layout->getBConds(); 00205 } 00206 void setBConds(const ParticleBConds<Position_t,PLayout::Dimension>& bc) { 00207 Layout->setBConds(bc); 00208 } 00209 00210 // Return a boolean value indicating if we are on a processor which can 00211 // be used for single-node particle creation and initialization 00212 bool singleInitNode() const; 00213 00214 // get or set the flags used to indicate what to do during the update 00215 bool getUpdateFlag(UpdateFlags f) const { 00216 return getLayout().getUpdateFlag(f); 00217 } 00218 void setUpdateFlag(UpdateFlags f, bool val) { 00219 getLayout().setUpdateFlag(f, val); 00220 } 00221 00222 // 00223 // attribute manipulation methods 00224 // 00225 00226 // add a new attribute ... called by constructor of this and derived classes 00227 void addAttribute(ParticleAttribBase& pa) { AttribList.push_back(&pa); } 00228 00229 // get a pointer to the base class for the Nth attribute 00230 ParticleAttribBase& 00231 getAttribute(attrib_container_t::size_type N) { return *(AttribList[N]); } 00232 00233 // return the number of attributes in our list 00234 attrib_container_t::size_type 00235 numAttributes() const { return AttribList.size(); } 00236 00237 // obtain the beginning and end iterators for our attribute list 00238 attrib_iterator begin() { return AttribList.begin(); } 00239 attrib_iterator end() { return AttribList.end(); } 00240 00241 // reset the particle ID's to be globally consecutive, 0 thru TotalNum-1. 00242 void resetID(); 00243 00244 // 00245 // Global operations on all attributes 00246 // 00247 00248 // Update the particle object after a timestep. This routine will change 00249 // our local, total, create particle counts properly. 00250 void update(); 00251 void update(const ParticleAttrib<char>& canSwap); 00252 00253 // create M new particles on this processor 00254 void create(size_t); 00255 00256 // create np new particles globally, equally distributed among all processors 00257 void globalCreate(size_t np); 00258 00259 // delete M particles, starting with the Ith particle. If the last argument 00260 // is true, the destroy will be done immediately, otherwise the request 00261 // will be cached. 00262 void destroy(size_t, size_t, bool = false); 00263 00264 // Put the data for M particles starting from local index I in a Message. 00265 // Return the number of particles put in the Message. 00266 size_t putMessage(Message&, size_t, size_t); 00267 // put the data for particles on a list into a Message, given list of indices 00268 // Return the number of particles put in the Message. 00269 size_t putMessage(Message&, const vector<size_t>&); 00270 00271 // Retrieve particles from the given message and store them. 00272 // Return the number of particles retrieved. 00273 size_t getMessage(Message&); 00274 00275 // retrieve particles from the given message and store them, also 00276 // signaling we are creating the given number of particles. Return the 00277 // number of particles created. 00278 size_t getMessageAndCreate(Message&); 00279 00280 // Actually perform the delete atoms action for all the attributes; the 00281 // calls to destroy() only stored a list of what to do. This actually 00282 // does it. This should in most cases only be called by the layout manager. 00283 void performDestroy(); 00284 00285 // Apply the given sortlist to all the attributes. 00286 void sort(SortList_t &); 00287 00288 // 00289 // Global operations on all ghost attributes ... generally, these should 00290 // only be used by the layout object 00291 // 00292 00293 // Put the data for M particles starting from local index I in a Message. 00294 // Return the number of particles put in the Message. This is for building 00295 // ghost particle interaction lists. 00296 size_t ghostPutMessage(Message&, size_t, size_t); 00297 00298 // put the data for particles on a list into a Message, given list of indices 00299 // Return the number of particles put in the Message. This is for building 00300 // ghost particle interaction lists. 00301 size_t ghostPutMessage(Message&, const vector<size_t>&); 00302 00303 // Retrieve particles from the given message and sending node and store them. 00304 // Return the number of particles retrieved. 00305 size_t ghostGetMessage(Message&, int); 00306 00307 // delete M ghost particles, starting with the Ith particle. This is 00308 // always done immediately. 00309 void ghostDestroy(size_t, size_t); 00310 00311 // 00312 // I/O 00313 // 00314 00315 // print out debugging information 00316 void printDebug(Inform&); 00317 00318 protected: 00319 // a virtual function which is called by this base class to get a 00320 // specific instance of DataSourceObject based on the type of data 00321 // and the connection method (the argument to the call). 00322 virtual DataSourceObject *createDataSourceObject(const char *nm, 00323 DataConnect *dc, int tm) { 00324 return make_DataSourceObject(nm, dc, tm, *this); 00325 } 00326 00327 private: 00328 // our layout object, which we delete in our destructor 00329 PLayout *Layout; 00330 00331 // our list of attributes 00332 attrib_container_t AttribList; 00333 00334 // our current number of total and local atoms, and 00335 // the number of particles we've deleted since the last update 00336 // also, the number of ghost particles 00337 size_t TotalNum; 00338 size_t LocalNum; 00339 size_t DestroyNum; 00340 size_t GhostNum; 00341 00342 // unique particle ID number generation value 00343 unsigned NextID; 00344 00345 // list of destroy events for the next update. The data 00346 // is not actually destroyed until the update phase. 00347 // Each destroy is stored as a pair of unsigned ints, the particle 00348 // index I to start at and the number of particles M to destroy. 00349 vector< pair<size_t,size_t> > DestroyList; 00350 00351 // 00352 // private methods 00353 // 00354 00355 // set up this new object: add attributes and check in to the layout 00356 void setup(); 00357 00358 // Return a new unique ID value for use by new particles. 00359 // The ID number = (i * numprocs) + myproc, i = 0, 1, 2, ... 00360 unsigned getNextID(); 00361 }; 00362 00363 #include "Particle/ParticleBase.cpp" 00364 00365 #endif // PARTICLE_BASE_H 00366 00367 /*************************************************************************** 00368 * $RCSfile: ParticleBase.h,v $ $Author: adelmann $ 00369 * $Revision: 1.1.1.1 $ $Date: 2003/01/23 07:40:28 $ 00370 * IPPL_VERSION_ID: $Id: ParticleBase.h,v 1.1.1.1 2003/01/23 07:40:28 adelmann Exp $ 00371 ***************************************************************************/