Branch data 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 : 204 : ~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 : 3144 : 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 : 120 : 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 : 12 : 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 : 96 : 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 : 0 : void forAllAttributes(Functor&& f) const {
200 : : if constexpr (std::is_void_v<MemorySpace>) {
201 : : attributes_m.forAll(f);
202 : : } else {
203 [ # # # # ]: 0 : for (auto& attribute : attributes_m.template get<MemorySpace>()) {
204 [ # # ]: 0 : f(attribute);
205 : : }
206 : : }
207 : 0 : }
208 : :
209 : : // Non-const variant of same function
210 : : template <typename MemorySpace = void, typename Functor>
211 : 180 : void forAllAttributes(Functor&& f) {
212 : : if constexpr (std::is_void_v<MemorySpace>) {
213 [ + - ]: 360 : attributes_m.forAll([&]<typename Attributes>(Attributes& atts) {
214 [ + + + + : 456 : for (auto& attribute : atts) {
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + ]
[ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + +
+ ]
[ # # # # ]
215 [ + - + - : 276 : f(attribute);
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - ]
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
[ # # # # ]
216 : : }
217 : : });
218 : : } else {
219 [ # # # # ]: 0 : for (auto& attribute : attributes_m.template get<MemorySpace>()) {
220 [ # # ]: 0 : f(attribute);
221 : : }
222 : : }
223 : 180 : }
224 : :
225 : : /*!
226 : : * @returns the number of attributes
227 : : */
228 : 12 : unsigned getAttributeNum() const {
229 : 12 : unsigned total = 0;
230 [ + - ]: 36 : detail::runForAllSpaces([&]<typename MemorySpace>() {
231 : 12 : total += attributes_m.template get<MemorySpace>().size();
232 : : });
233 : 12 : 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 : 84 : 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
|