BioCMAMC-ST
particles_container.hpp
1#ifndef __PARTICLES_CONTAINER_HPP__
2#define __PARTICLES_CONTAINER_HPP__
3
4#include "Kokkos_Core_fwd.hpp"
5#include "common/common.hpp"
6#include <Kokkos_Core.hpp>
7#include <common/has_serialize.hpp>
8#include <cstdint>
9#include <mc/prng/prng.hpp>
10#include <mc/traits.hpp>
11#include <stdexcept>
12#include <type_traits>
13
14namespace
15{
16 template <ModelType M> struct FillGapFunctor
17 {
18
19 FillGapFunctor(MC::ParticleStatus _status,
20 M::SelfParticle _model,
21 MC::ParticlePositions _position,
22 std::size_t _to_remove,
23 std::size_t _last_used_index)
24 : status(std::move(_status)), model(std::move(_model)), position(std::move(_position)),
25 offset("offset"), to_remove(_to_remove), last_used_index(_last_used_index)
26 {
27
28 Kokkos::deep_copy(offset, 0);
29 }
30
31 KOKKOS_INLINE_FUNCTION void operator()(const int i, std::size_t& update, const bool final) const
32 {
33 const bool is_dead = (status(i) != MC::Status::Idle);
34
35 std::size_t scan_index = update;
36 update += is_dead ? 1 : 0;
37
38 if (final && is_dead && scan_index < to_remove)
39 {
40
41 const auto i_to_remove = i;
42 // Kokkos::printf("Removing index %d\r\n", i_to_remove);
43
44 auto idx_to_move = last_used_index - Kokkos::atomic_fetch_add(&offset(), 1);
45 while (status(idx_to_move) != MC::Status::Idle ||
46 idx_to_move == static_cast<std::size_t>(i_to_remove))
47 {
48 idx_to_move = last_used_index - Kokkos::atomic_fetch_add(&offset(), 1);
49 }
50
51 status(i_to_remove) = MC::Status::Idle;
52 Kokkos::atomic_exchange(&position(i_to_remove), position(idx_to_move));
53 // TODO Use hierachical parallism here, thread range is likely to work
54 for (std::size_t i_properties = 0; i_properties < M::n_var; ++i_properties)
55 {
56 model(i_to_remove, i_properties) = model(idx_to_move, i_properties);
57 }
58 }
59 }
60
61 MC::ParticleStatus status;
62 M::SelfParticle model;
63 MC::ParticlePositions position;
64 Kokkos::View<std::size_t, ComputeSpace> offset;
65 std::size_t to_remove;
66 std::size_t last_used_index;
67 };
68 using TeamPolicy = Kokkos::TeamPolicy<ComputeSpace>;
69 using TeamMember = TeamPolicy::member_type;
70 template <ModelType M> struct InsertFunctor
71 {
72 InsertFunctor(std::size_t _original_size,
73 M::SelfParticle _model,
74 MC::ParticlePositions _position,
75 M::SelfParticle _buffer_model,
76 MC::ParticlePositions _buffer_position)
77 : original_size(_original_size), model(std::move(_model)), position(std::move(_position)),
78 buffer_model(std::move(_buffer_model)), buffer_position(std::move(_buffer_position))
79 {
80 }
81
82 // KOKKOS_INLINE_FUNCTION void operator()(const int i) const
83 // {
84 // position(original_size + i) = buffer_position(i);
85 // for (std::size_t j = 0; j < M::n_var; ++j)
86 // {
87 // model(original_size + i, j) = buffer_model(i, j);
88 // }
89 // }
90
91 // TODO try this functor
92 // TODO find a way to organise data to not copy non needed data (like contribs). Split model in
93 // two arrays?
94
95 KOKKOS_INLINE_FUNCTION
96 void operator()(const TeamMember& team) const
97 {
98 auto range = M::n_var;
99 const int i = team.league_rank();
100
101 position(original_size + i) = buffer_position(i);
102 Kokkos::parallel_for(Kokkos::TeamVectorRange(team, range),
103 [&](const int& j) { model(original_size + i, j) = buffer_model(i, j); });
104 }
105
106 std::size_t original_size;
107 M::SelfParticle model;
108 MC::ParticlePositions position;
109 M::SelfParticle buffer_model;
110 MC::ParticlePositions buffer_position;
111 };
112
113}; // namespace
114
115namespace MC
116{
127 template <ModelType Model> class ParticlesContainer
128 {
129 public:
130 static constexpr double buffer_ratio = 0.6; // Buffer size = ceil(list.size()*buffer_ratio)
134 using UsedModel = Model;
135
136 explicit ParticlesContainer(std::size_t n_particle);
137 ParticlesContainer(); //=default;
138
139 Model::SelfParticle model;
150
155
161 [[nodiscard]] KOKKOS_INLINE_FUNCTION std::size_t n_particles() const
162 {
163 return n_used_elements;
164 };
165
166 KOKKOS_INLINE_FUNCTION void get_contributions(std::size_t idx,
167 const ContributionView& contributions) const;
168
169 [[nodiscard]] KOKKOS_INLINE_FUNCTION bool
170 handle_division(const MC::KPRNG::pool_type& random_pool, std::size_t idx1) const;
171
172 void clean_dead(std::size_t to_remove);
173
175
176 [[maybe_unused]] [[nodiscard]] auto get_buffer_index() const
177 {
178 return buffer_index();
179 }
180
181 template <class Archive> void save(Archive& ar) const
182 {
184 serialize_view(ar, weights);
185 serialize_view(ar, position);
186 serialize_view(ar, status);
187 serialize_view(ar, model);
188 }
189
190 template <class Archive> void load(Archive& ar)
191 {
193 deserialize_view(ar, weights);
194 deserialize_view(ar, position);
195 deserialize_view(ar, status);
196 deserialize_view(ar, model);
198 }
199
200 [[nodiscard]] KOKKOS_INLINE_FUNCTION double get_weight(std::size_t idx) const;
201
202 [[nodiscard]] double get_allocation_factor() const
203 {
204 return allocation_factor;
205 }
206
207 [[nodiscard]] std::size_t capacity() const
208 {
210 }
211
212 private:
213 Model::SelfParticle buffer_model;
215 Kokkos::View<uint64_t, Kokkos::SharedSpace> buffer_index;
219
220 static constexpr double default_allocation_factor = 2;
221 void __allocate__(std::size_t new_size);
223 void __shrink__(std::size_t new_size, bool force);
224 };
225
226 template <ModelType Model>
227 KOKKOS_INLINE_FUNCTION bool
229 std::size_t idx1) const
230 {
231 if (Kokkos::atomic_load(&buffer_index()) < buffer_model.extent(0))
232 {
233 const auto idx2 = Kokkos::atomic_fetch_add(&buffer_index(), 1);
234 Model::division(random_pool, idx1, idx2, model, buffer_model);
235 buffer_position(idx2) = position(idx1);
236
237 return true;
238 }
239 Kokkos::printf("%ld %ld\r\n", Kokkos::atomic_load(&buffer_index()), buffer_model.extent(0));
240 return false;
241 }
242
243 template <ModelType Model> void ParticlesContainer<Model>::merge_buffer()
244 {
245 PROFILE_SECTION("ParticlesContainer::merge_buffer")
246 const auto original_size = n_used_elements;
247 const auto n_add_item = buffer_index();
248 if (n_add_item == 0)
249 {
250 return;
251 }
252 __allocate__(original_size + n_add_item);
253 // Merge position EZ
254
255 // Kokkos::parallel_for(
256 // "InsertMerge",
257 // Kokkos::RangePolicy<>(0, n_add_item),
258 // InsertFunctor<Model>(original_size, model, position, buffer_model, buffer_position));
259
260 auto get_policy_insert = [=]()
261 {
262 if constexpr (std::is_same_v<Kokkos::DefaultHostExecutionSpace, Kokkos::DefaultExecutionSpace>)
263 {
264 return TeamPolicy(n_add_item/Kokkos::num_threads(), Kokkos::AUTO, Model::n_var);
265 }
266 else
267 {
268 return TeamPolicy(n_add_item, Kokkos::AUTO, Model::n_var);
269 }
270 };
271
272
273 Kokkos::parallel_for(
274 "InsertMerge",
275 TeamPolicy(n_add_item, Kokkos::AUTO, Model::n_var),
276 InsertFunctor<Model>(original_size, model, position, buffer_model, buffer_position));
277
278 buffer_index() = 0;
279 n_used_elements += n_add_item;
280 __allocate_buffer__();
281 }
282
283 template <ModelType Model>
284 void ParticlesContainer<Model>::__allocate__(const std::size_t new_size)
285 {
286 PROFILE_SECTION("ParticlesContainer::__allocate__")
287 if (new_size > 0)
288 {
289 if (new_size >= n_allocated_elements)
290 {
291 const auto new_allocated_size =
292 static_cast<std::size_t>(std::ceil(static_cast<double>(new_size) * allocation_factor));
293 n_allocated_elements = new_allocated_size;
294 Kokkos::resize(position, n_allocated_elements);
295 Kokkos::resize(model, n_allocated_elements,Model::n_var);//use 2nd dim resize if dynamic
296 Kokkos::resize(status, n_allocated_elements);
297 if constexpr (ConstWeightModelType<Model>)
298 {
299 Kokkos::resize(weights, 1);
300 }
301 else
302 {
303 Kokkos::resize(weights, n_allocated_elements);
304 }
305 }
306 }
307 }
308
309 template <ModelType Model>
310 void ParticlesContainer<Model>::__shrink__(std::size_t new_size, bool force)
311 {
312 PROFILE_SECTION("ParticlesContainer::__shrink__")
313 if (new_size > 0 && (new_size > n_used_elements || force))
314 {
315 const auto new_allocated_size =
316 static_cast<std::size_t>(std::ceil(static_cast<double>(new_size) * allocation_factor));
317 n_allocated_elements = new_allocated_size;
318 Kokkos::resize(position, n_allocated_elements);
319 Kokkos::resize(model, n_allocated_elements,Model::n_var); //use 2nd dim resize if dynamic
320 Kokkos::resize(status, n_allocated_elements);
321 if constexpr (ConstWeightModelType<Model>)
322 {
323 Kokkos::resize(weights, 1);
324 }
325 else
326 {
327 Kokkos::resize(weights, n_allocated_elements);
328 }
329 }
330 }
331
332 template <ModelType Model> void ParticlesContainer<Model>::__allocate_buffer__()
333 {
334 PROFILE_SECTION("ParticlesContainer::__allocate_buffer__")
335 auto buffer_size = buffer_position.extent(0);
336 if (static_cast<double>(buffer_size) / static_cast<double>(n_allocated_elements) < buffer_ratio)
337 {
338 buffer_size = static_cast<std::size_t>(
339 std::ceil(static_cast<double>(n_allocated_elements) * buffer_ratio));
340
341 // Realloc because not needed to keep buffer as it has been copied
342 Kokkos::realloc(buffer_position, buffer_size);
343 Kokkos::realloc(buffer_model, buffer_size,Model::n_var); //use 2nd dim resize if dynamic
344 buffer_index() = 0;
345 }
346 }
347
348 template <ModelType M>
350 : model(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_model"), 0),
351 position(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_position"), 0),
352 status(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_status"), 0),
353 weights(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_weigth"), 0),
354 buffer_model("buffer_particle_model", 0),
355 buffer_position("buffer_particle_position", 0), // Dont allocate now
356 buffer_index("buffer_index"), allocation_factor(default_allocation_factor),
357 n_allocated_elements(0), n_used_elements(n_particle)
358 {
359
360 __allocate__(n_particle);
362 }
363
364 template <ModelType M>
366 : model(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_model"), 0),
367 position(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_position"), 0),
368 status(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_status"), 0),
369 weights(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_weigth"), 0),
370
371 buffer_model(Kokkos::view_alloc(Kokkos::WithoutInitializing, "buffer_particle_model"), 0),
372 buffer_position(Kokkos::view_alloc(Kokkos::WithoutInitializing, "buffer_particle_model")),
373 buffer_index("buffer_index"), allocation_factor(default_allocation_factor),
374 n_allocated_elements(0), n_used_elements(0)
375
376 {
377 }
378
379 template <ModelType M> void ParticlesContainer<M>::clean_dead(std::size_t to_remove)
380 {
381 PROFILE_SECTION("ParticlesContainer::remove_dead")
382 if (to_remove == 0)
383 {
384 return;
385 }
386
387 if (to_remove == n_used_elements)
388 {
389 __shrink__(0, true);
390 n_used_elements = 0;
391 }
392 else if (to_remove > n_used_elements)
393 {
394 throw std::runtime_error(
395 "clean_dead: Error in kernel cannot remove more element than existing");
396 }
397 else
398 {
399
400 const auto new_used_item = n_used_elements - to_remove;
401 // Kokkos::printf("Sorting \r\n");
402 // std::cout << "n_used_elements " << n_used_elements << std::endl;
403 // std::cout << "To remove " << to_remove << std::endl;
404
405 const auto last_used_index = n_used_elements - 1;
406 Kokkos::parallel_scan("find_and_fill_gap",
407 Kokkos::RangePolicy<ComputeSpace>(0, n_used_elements),
408 FillGapFunctor<M>(status, model, position, to_remove, last_used_index));
409
410 // Kokkos::View<std::size_t, ComputeSpace> offset("offset");
411 // Kokkos::deep_copy(offset, 0);
412 // Kokkos::parallel_scan(
413 // "find_and_fill_gap",
414 // Kokkos::RangePolicy<ComputeSpace>(0, n_used_elements),
415 // KOKKOS_CLASS_LAMBDA(const int i, std::size_t& update, const bool final) {
416 // const bool is_dead = (status(i) != Status::Idle);
417
418 // std::size_t scan_index = update;
419 // update += is_dead ? 1 : 0;
420
421 // if (final && is_dead && scan_index < to_remove)
422 // {
423
424 // const auto i_to_remove = i;
425 // // Kokkos::printf("Removing index %d\r\n", i_to_remove);
426
427 // auto idx_to_move = last_used_index - Kokkos::atomic_fetch_add(&offset(), 1);
428 // while (status(idx_to_move) != Status::Idle || idx_to_move == i_to_remove)
429 // {
430 // idx_to_move = last_used_index - Kokkos::atomic_fetch_add(&offset(), 1);
431 // }
432
433 // status(i_to_remove) = Status::Idle;
434 // Kokkos::atomic_exchange(&position(i_to_remove), position(idx_to_move));
435
436 // for (std::size_t i_properties = 0; i_properties < M::n_var; ++i_properties)
437 // {
438 // model(i_to_remove, i_properties) = model(idx_to_move, i_properties);
439 // }
440 // }
441 // });
442 Kokkos::fence();
443 KOKKOS_ASSERT(this->position.extent(0) == n_allocated_elements);
444 KOKKOS_ASSERT(this->model.extent(0) == n_allocated_elements);
445 KOKKOS_ASSERT(this->status.extent(0) == n_allocated_elements);
446 n_used_elements = new_used_item;
447 if (static_cast<double>(n_used_elements) / static_cast<double>(n_allocated_elements) <= 0.1)
448 {
449 // std::cout << "SHRINK" << std::endl;
450 __shrink__(n_used_elements * 2, false);
451 }
452 };
453 }
454
455 template <ModelType M>
456 KOKKOS_INLINE_FUNCTION void
458 const ContributionView& contributions) const
459 {
460 static_assert(ConstWeightModelType<M>, "ModelType: Const apply_weight()");
461 const double weight = get_weight(idx);
462 M::contribution(idx, position(idx), weight, model, contributions);
463 }
464
465 template <ModelType M>
466 [[nodiscard]] KOKKOS_INLINE_FUNCTION double
468 {
469 if constexpr (ConstWeightModelType<M>)
470 {
471 return weights(0);
472 }
473 else
474 {
475 return weights(idx);
476 }
477 }
478
479} // namespace MC
480
481#endif
Kokkos::Random_XorShift1024_Pool< Kokkos::DefaultExecutionSpace > pool_type
Definition prng.hpp:17
Main owning object for Monte-Carlo particles.
Definition particles_container.hpp:128
~ParticlesContainer()=default
Default destructor.
void __allocate__(std::size_t new_size)
Definition particles_container.hpp:284
Model::SelfParticle model
Definition particles_container.hpp:139
KOKKOS_INLINE_FUNCTION void get_contributions(std::size_t idx, const ContributionView &contributions) const
Definition particles_container.hpp:457
KOKKOS_INLINE_FUNCTION std::size_t n_particles() const
Gets the number of particles in the container.
Definition particles_container.hpp:161
ParticlesContainer(std::size_t n_particle)
Definition particles_container.hpp:349
double allocation_factor
Definition particles_container.hpp:216
ParticleWeigths weights
Definition particles_container.hpp:142
double get_allocation_factor() const
Definition particles_container.hpp:202
Kokkos::View< uint64_t, Kokkos::SharedSpace > buffer_index
Definition particles_container.hpp:215
void __allocate_buffer__()
Definition particles_container.hpp:332
ParticlesContainer()
Definition particles_container.hpp:365
KOKKOS_INLINE_FUNCTION bool handle_division(const MC::KPRNG::pool_type &random_pool, std::size_t idx1) const
Definition particles_container.hpp:228
ParticlesContainer & operator=(ParticlesContainer &&)=default
void __shrink__(std::size_t new_size, bool force)
Definition particles_container.hpp:310
uint64_t n_used_elements
Definition particles_container.hpp:218
auto get_buffer_index() const
Definition particles_container.hpp:176
void merge_buffer()
Definition particles_container.hpp:243
Model::SelfParticle buffer_model
Definition particles_container.hpp:213
KOKKOS_INLINE_FUNCTION double get_weight(std::size_t idx) const
Definition particles_container.hpp:467
std::size_t capacity() const
Definition particles_container.hpp:207
ParticlePositions buffer_position
Definition particles_container.hpp:214
MC::ParticlePositions position
Definition particles_container.hpp:140
void save(Archive &ar) const
Definition particles_container.hpp:181
static constexpr double default_allocation_factor
Definition particles_container.hpp:220
static constexpr double buffer_ratio
Definition particles_container.hpp:130
Model UsedModel
Alias for the model used by the container.
Definition particles_container.hpp:134
void clean_dead(std::size_t to_remove)
Definition particles_container.hpp:379
ParticlesContainer & operator=(const ParticlesContainer &)=default
ParticlesContainer(const ParticlesContainer &)=default
Default copy and move constructors and assignment operators.
void load(Archive &ar)
Definition particles_container.hpp:190
ParticlesContainer(ParticlesContainer &&)=default
std::size_t n_allocated_elements
Definition particles_container.hpp:217
MC::ParticleStatus status
Definition particles_container.hpp:141
Definition traits.hpp:115
Namespace that contains classes and structures related to Monte Carlo (MC) simulations.
Definition alias.hpp:9
Kokkos::View< double *, ComputeSpace > ParticleWeigths
Definition alias.hpp:21
Kokkos::View< Status *, ComputeSpace > ParticleStatus
Definition alias.hpp:20
Kokkos::View< uint64_t *, ComputeSpace > ParticlePositions
Definition alias.hpp:19
Kokkos::Experimental::ScatterView< double **, Kokkos::LayoutRight > ContributionView
Definition alias.hpp:30