Line data Source code
1 : //
2 : // Class ParticleBase
3 : // Base class for all user-defined particle classes.
4 : //
5 : // ParticleBase is a container and manager for a set of particles.
6 : // The user must define a class derived from ParticleBase which describes
7 : // what specific data attributes the particle has (e.g., mass or charge).
8 : // Each attribute is an instance of a ParticleAttribute<T> class; ParticleBase
9 : // keeps a list of pointers to these attributes, and performs particle creation
10 : // and destruction.
11 : //
12 : // ParticleBase is templated on the ParticleLayout mechanism for the particles.
13 : // This template parameter should be a class derived from ParticleLayout.
14 : // ParticleLayout-derived classes maintain the info on which particles are
15 : // located on which processor, and performs the specific communication
16 : // required between processors for the particles. The ParticleLayout is
17 : // templated on the type and dimension of the atom position attribute, and
18 : // ParticleBase uses the same types for these items as the given
19 : // ParticleLayout.
20 : //
21 : // ParticleBase and all derived classes have the following common
22 : // characteristics:
23 : // - The spatial positions of the N particles are stored in the
24 : // particle_position_type variable R
25 : // - The global index of the N particles are stored in the
26 : // particle_index_type variable ID
27 : // - A pointer to an allocated layout class. When you construct a
28 : // ParticleBase, you must provide a layout instance, and ParticleBase
29 : // will delete this instance when it (the ParticleBase) is deleted.
30 : //
31 : // To use this class, the user defines a derived class with the same
32 : // structure as in this example:
33 : //
34 : // class UserParticles :
35 : // public ParticleBase< ParticleSpatialLayout<double,3> > {
36 : // public:
37 : // // attributes for this class
38 : // ParticleAttribute<double> rad; // radius
39 : // particle_position_type vel; // velocity, same storage type as R
40 : //
41 : // // constructor: add attributes to base class
42 : // UserParticles(ParticleSpatialLayout<double,2>* L) : ParticleBase(L) {
43 : // addAttribute(rad);
44 : // addAttribute(vel);
45 : // }
46 : // };
47 : //
48 : // This example defines a user class with 3D position and two extra
49 : // attributes: a radius rad (double), and a velocity vel (a 3D Vector).
50 : //
51 : #ifndef IPPL_PARTICLE_BASE_H
52 : #define IPPL_PARTICLE_BASE_H
53 :
54 : #include <tuple>
55 : #include <type_traits>
56 : #include <vector>
57 :
58 : #include "Types/IpplTypes.h"
59 :
60 : #include "Utility/TypeUtils.h"
61 :
62 : #include "Particle/ParticleLayout.h"
63 :
64 : namespace ippl {
65 :
66 : /*!
67 : * @class ParticleBase
68 : * @tparam PLayout the particle layout implementing an algorithm to
69 : * distribute the particles among MPI ranks
70 : * @tparam IDProperties the view properties for particle IDs (if any
71 : * of the provided types is ippl::DisableParticleIDs, then particle
72 : * IDs will be disabled for the bunch)
73 : */
74 : template <class PLayout, typename... IDProperties>
75 : class ParticleBase {
76 : constexpr static bool EnableIDs = sizeof...(IDProperties) > 0;
77 :
78 : public:
79 : using vector_type = typename PLayout::vector_type;
80 : using index_type = typename PLayout::index_type;
81 : using particle_position_type = typename PLayout::particle_position_type;
82 : using particle_index_type = ParticleAttrib<index_type, IDProperties...>;
83 :
84 : using Layout_t = PLayout;
85 :
86 : template <typename... Properties>
87 : using attribute_type = typename detail::ParticleAttribBase<Properties...>;
88 :
89 : template <typename MemorySpace>
90 : using container_type = std::vector<attribute_type<MemorySpace>*>;
91 :
92 : using attribute_container_type =
93 : typename detail::ContainerForAllSpaces<container_type>::type;
94 :
95 : using bc_container_type = typename PLayout::bc_container_type;
96 :
97 : using hash_container_type = typename detail::ContainerForAllSpaces<detail::hash_type>::type;
98 :
99 : using size_type = detail::size_type;
100 :
101 : public:
102 : //! view of particle positions
103 : particle_position_type R;
104 :
105 : //! view of particle IDs
106 : particle_index_type ID;
107 :
108 : /*!
109 : * If this constructor is used, the user must call 'initialize' with
110 : * a layout object in order to use this.
111 : */
112 : ParticleBase();
113 :
114 : /*!
115 : * Ctor called when layout is provided with std::shared_ptr. It
116 : * calls the default ctor which then calls the private ctor. The
117 : * layout instance is moved to this class, hence, the argument
118 : * is null afterwards, i.e., layout == nullptr.
119 : * @param layout to be moved.
120 : */
121 : ParticleBase(Layout_t& layout);
122 :
123 : /* cannot use '= default' since we get a
124 : * compiler warning otherwise:
125 : * warning: calling a __host__ function("std::vector< ::ippl::detail::ParticleAttribBase *,
126 : * ::std::allocator<
127 : * ::ippl::detail::ParticleAttribBase *> > ::~vector") from a __host__ __device__
128 : * function("ippl::ParticleBase<
129 : * ::ippl::ParticleLayout<double, (unsigned int)3u> > ::~ParticleBase") is not allowed
130 : */
131 408 : ~ParticleBase() {} // = default; //{ }
132 :
133 : /*!
134 : * Initialize the particle layout. Needs to be called
135 : * when the ParticleBase instance is constructed with the
136 : * default ctor.
137 : */
138 : void initialize(Layout_t& layout);
139 :
140 : /*!
141 : * @returns processor local number of particles
142 : */
143 3528 : size_type getLocalNum() const { return localNum_m; }
144 :
145 : void setLocalNum(size_type size) { localNum_m = size; }
146 :
147 : /*!
148 : * @returns total number of particles (across all processes)
149 : */
150 : size_type getTotalNum() const { return totalNum_m; }
151 :
152 : /*!
153 : * @returns particle layout
154 : */
155 240 : Layout_t& getLayout() { return *layout_m; }
156 :
157 : /*!
158 : * @returns particle layout
159 : */
160 : const Layout_t& getLayout() const { return *layout_m; }
161 :
162 : /*!
163 : * Set all boundary conditions
164 : * @param bc the boundary conditions
165 : */
166 24 : void setParticleBC(const bc_container_type& bcs) { layout_m->setParticleBC(bcs); }
167 :
168 : /*!
169 : * Set all boundary conditions to this BC
170 : * @param bc the boundary conditions
171 : */
172 192 : void setParticleBC(BC bc) { layout_m->setParticleBC(bc); }
173 :
174 : /*!
175 : * Add particle attribute
176 : * @param pa attribute to be added to ParticleBase
177 : */
178 : template <typename MemorySpace>
179 : void addAttribute(detail::ParticleAttribBase<MemorySpace>& pa);
180 :
181 : /*!
182 : * Get particle attribute
183 : * @param i attribute number in container
184 : * @returns a pointer to the attribute
185 : */
186 : template <typename MemorySpace = Kokkos::DefaultExecutionSpace::memory_space>
187 : attribute_type<MemorySpace>* getAttribute(size_t i) {
188 : return attributes_m.template get<MemorySpace>()[i];
189 : }
190 :
191 : /*!
192 : * Calls a given function for all attributes in the bunch
193 : * @tparam MemorySpace the memory space of the attributes to visit (void to visit all of
194 : * them)
195 : * @tparam Functor the functor type
196 : * @param f a functor taking a single ParticleAttrib<MemorySpace>
197 : */
198 : template <typename MemorySpace = void, typename Functor>
199 264 : void forAllAttributes(Functor&& f) const {
200 : if constexpr (std::is_void_v<MemorySpace>) {
201 : attributes_m.forAll(f);
202 : } else {
203 840 : for (auto& attribute : attributes_m.template get<MemorySpace>()) {
204 576 : f(attribute);
205 : }
206 : }
207 264 : }
208 :
209 : // Non-const variant of same function
210 : template <typename MemorySpace = void, typename Functor>
211 708 : void forAllAttributes(Functor&& f) {
212 : if constexpr (std::is_void_v<MemorySpace>) {
213 888 : attributes_m.forAll([&]<typename Attributes>(Attributes& atts) {
214 1188 : for (auto& attribute : atts) {
215 744 : f(attribute);
216 : }
217 : });
218 : } else {
219 840 : for (auto& attribute : attributes_m.template get<MemorySpace>()) {
220 576 : f(attribute);
221 : }
222 : }
223 708 : }
224 :
225 : /*!
226 : * @returns the number of attributes
227 : */
228 24 : unsigned getAttributeNum() const {
229 24 : unsigned total = 0;
230 72 : detail::runForAllSpaces([&]<typename MemorySpace>() {
231 24 : total += attributes_m.template get<MemorySpace>().size();
232 : });
233 24 : return total;
234 : }
235 :
236 : /*!
237 : * Create nLocal processor local particles. This is a collective call,
238 : * i.e. all MPI ranks must call this.
239 : * @param nLocal number of local particles to be created
240 : */
241 : void create(size_type nLocal);
242 :
243 : /*!
244 : * Create a new particle with a given ID. This is a collective call. If a process
245 : * passes a negative number, it does not create a particle.
246 : * @param id particle identity number
247 : */
248 : void createWithID(index_type id);
249 :
250 : /*!
251 : * Create nTotal particles globally, equally distributed among all processors.
252 : * This is a collective call.
253 : * @param nTotal number of total particles to be created
254 : */
255 : void globalCreate(size_type nTotal);
256 :
257 : /*!
258 : * Particle deletion Function. Partition the particles into a valid region
259 : * and an invalid region,
260 : * effectively deleting the invalid particles. This is a collective call.
261 : * @param invalid View marking which indices are invalid
262 : * @param destroyNum Total number of invalid particles
263 : */
264 : template <typename... Properties>
265 : void destroy(const Kokkos::View<bool*, Properties...>& invalid, const size_type destroyNum);
266 :
267 : // This is a collective call.
268 168 : void update() { layout_m->update(*this); }
269 :
270 : /*
271 : * The following functions should not be called in an application.
272 : */
273 :
274 : /* This function does not alter the totalNum_m member function. It should only be called
275 : * during the update function where we know the number of particles remains the same.
276 : */
277 : template <typename... Properties>
278 : void internalDestroy(const Kokkos::View<bool*, Properties...>& invalid,
279 : const size_type destroyNum);
280 :
281 : /*!
282 : * Sends particles to another rank
283 : * @tparam HashType the hash view type
284 : * @param rank the destination rank
285 : * @param tag the MPI tag
286 : * @param sendNum the number of messages already sent (to distinguish the buffers)
287 : * @param requests destination vector in which to store the MPI requests for polling
288 : * purposes
289 : * @param hash a hash view indicating which particles need to be sent to which rank
290 : */
291 : template <typename HashType>
292 : void sendToRank(int rank, int tag, std::vector<MPI_Request>& requests,
293 : const HashType& hash);
294 :
295 : /*!
296 : * Receives particles from another rank
297 : * @param rank the source rank
298 : * @param tag the MPI tag
299 : * @param recvNum the number of messages already received (to distinguish the buffers)
300 : * @param nRecvs the number of particles to receive
301 : */
302 : void recvFromRank(int rank, int tag, size_type nRecvs);
303 :
304 : /*!
305 : * Serialize to do MPI calls.
306 : * @param ar archive
307 : */
308 : template <typename Archive>
309 : void serialize(Archive& ar, size_type nsends);
310 :
311 : /*!
312 : * Deserialize to do MPI calls.
313 : * @param ar archive
314 : */
315 : template <typename Archive>
316 : void deserialize(Archive& ar, size_type nrecvs);
317 :
318 : /*!
319 : * Determine the total space necessary to store a certain number of particles
320 : * @tparam MemorySpace only consider attributes stored in this memory space
321 : * @param count particle number
322 : * @return Total size of a buffer packed with the given number of particles
323 : */
324 : template <typename MemorySpace>
325 : size_type packedSize(const size_type count) const;
326 :
327 : protected:
328 : /*!
329 : * Fill attributes of buffer.
330 : * @param buffer to send
331 : * @param hash function to access index.
332 : */
333 : void pack(const hash_container_type& hash);
334 :
335 : /*!
336 : * Fill my attributes.
337 : * @param buffer received
338 : */
339 : void unpack(size_type nrecvs);
340 :
341 : private:
342 : //! particle layout
343 : // cannot use std::unique_ptr due to Kokkos
344 : Layout_t* layout_m;
345 :
346 : //! processor local number of particles
347 : size_type localNum_m;
348 :
349 : //! total number of particles (across all processes)
350 : size_type totalNum_m;
351 :
352 : //! all attributes
353 : attribute_container_type attributes_m;
354 :
355 : //! next unique particle ID
356 : index_type nextID_m;
357 :
358 : //! number of MPI ranks
359 : index_type numNodes_m;
360 :
361 : //! buffers for particle partitioning
362 : hash_container_type deleteIndex_m;
363 : hash_container_type keepIndex_m;
364 : };
365 : } // namespace ippl
366 :
367 : #include "Particle/ParticleBase.hpp"
368 :
369 : #endif
|