BioCMAMC-ST
particles_container.hpp
1#ifndef __PARTICLES_CONTAINER_HPP__
2#define __PARTICLES_CONTAINER_HPP__
3
4#include "common/common.hpp"
5#include "mc/alias.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
13namespace
14{
15
16 using TeamPolicy = Kokkos::TeamPolicy<ComputeSpace>;
17 using TeamMember = TeamPolicy::member_type;
18
19 template <ModelType M> struct FillGapFunctor
20 {
21
22 FillGapFunctor(MC::ParticleStatus _status,
23 M::SelfParticle _model,
24 MC::ParticlePositions _position,
25 MC::ParticleAges _ages,
26 std::size_t _to_remove,
27 std::size_t _last_used_index)
28 : status(std::move(_status)), model(std::move(_model)),
29 position(std::move(_position)), ages(std::move(_ages)),
30 offset("offset"), to_remove(_to_remove),
31 last_used_index(_last_used_index)
32 {
33
34 Kokkos::deep_copy(offset, 0);
35 }
36
37 KOKKOS_INLINE_FUNCTION void
38 operator()(const int i, std::size_t& update, const bool final) const
39 {
40 const bool is_dead = (status(i) != MC::Status::Idle);
41
42 std::size_t scan_index = update;
43 update += is_dead ? 1 : 0;
44
45 if (final && is_dead && scan_index < to_remove)
46 {
47
48 const auto i_to_remove = i;
49 auto idx_to_move =
50 last_used_index - Kokkos::atomic_fetch_add(&offset(), 1);
51 while (status(idx_to_move) != MC::Status::Idle ||
52 idx_to_move == static_cast<std::size_t>(i_to_remove))
53 {
54 idx_to_move =
55 last_used_index - Kokkos::atomic_fetch_add(&offset(), 1);
56 }
57
58 status(i_to_remove) = MC::Status::Idle;
59 Kokkos::atomic_exchange(&position(i_to_remove), position(idx_to_move));
60 // TODO Use hierachical parallism here, thread range is likely to work
61 for (std::size_t i_properties = 0; i_properties < M::n_var;
62 ++i_properties)
63 {
64 model(i_to_remove, i_properties) = model(idx_to_move, i_properties);
65 }
66 ages(i_to_remove, 0) = ages(idx_to_move, 0);
67 ages(i_to_remove, 1) = ages(idx_to_move, 1);
68 }
69 }
70
71 MC::ParticleStatus status;
72 M::SelfParticle model;
73 MC::ParticlePositions position;
75 Kokkos::View<std::size_t, ComputeSpace> offset;
76 std::size_t to_remove;
77 std::size_t last_used_index;
78 };
79
80 template <ModelType M> struct InsertFunctor
81 {
82 InsertFunctor(std::size_t _original_size,
83 M::SelfParticle _model,
84 MC::ParticlePositions _position,
85 M::SelfParticle _buffer_model,
86 MC::ParticlePositions _buffer_position)
87 : original_size(_original_size), model(std::move(_model)),
88 position(std::move(_position)),
89 buffer_model(std::move(_buffer_model)),
90 buffer_position(std::move(_buffer_position))
91 {
92 }
93
94 // KOKKOS_INLINE_FUNCTION void operator()(const int i) const
95 // {
96 // position(original_size + i) = buffer_position(i);
97 // for (std::size_t j = 0; j < M::n_var; ++j)
98 // {
99 // model(original_size + i, j) = buffer_model(i, j);
100 // }
101 // }
102
103 // TODO try this functor
104 // TODO find a way to organise data to not copy non needed data (like
105 // contribs). Split model in two arrays?
106
107 KOKKOS_INLINE_FUNCTION
108 void operator()(const TeamMember& team) const
109 {
110 auto range = M::n_var;
111 const int i = team.league_rank();
112
113 position(original_size + i) = buffer_position(i);
114 Kokkos::parallel_for(
115 Kokkos::TeamVectorRange(team, range),
116 [&](const int& j)
117 { model(original_size + i, j) = buffer_model(i, j); });
118 }
119
120 std::size_t original_size;
121 M::SelfParticle model;
122 MC::ParticlePositions position;
123 M::SelfParticle buffer_model;
124 MC::ParticlePositions buffer_position;
125 };
126
127}; // namespace
128
129namespace MC
130{
142 template <ModelType Model> class ParticlesContainer
143 {
144 public:
145 static constexpr double buffer_ratio =
146 1;
148
151 using UsedModel = Model;
152
153 explicit ParticlesContainer(std::size_t n_particle,
154 bool virtual_position = false);
155 ParticlesContainer(); //=default;
156
157 Model::SelfParticle model;
169
174
180 [[nodiscard]] KOKKOS_INLINE_FUNCTION std::size_t n_particles() const;
181
182 // TODO Add logic off internal_dead_counter header
183
184 KOKKOS_INLINE_FUNCTION void
185 get_contributions(std::size_t idx,
186 const ContributionView& contributions) const;
187
188 // template <typename ExecutionSpace, typename TeamHandle>
189 // KOKKOS_INLINE_FUNCTION typename std::enable_if<
190 // std::is_same<ExecutionSpace, Kokkos::HostSpace>::value>::type
191 // get_contributions(const MC::KPRNG::pool_type& random_pool,
192 // std::size_t idx,
193 // const ContributionView& contributions,
194 // const TeamMember& handle) const;
195
196 // template <typename ExecutionSpace, typename TeamHandle>
197 // typename std::enable_if<
198 // !std::is_same<ExecutionSpace, Kokkos::HostSpace>::value>::type
199 // get_contributions(const MC::KPRNG::pool_type& random_pool,
200 // std::size_t idx,
201 // const ContributionView& contributions,
202 // const TeamMember& handle) const;
203
204 [[nodiscard]] KOKKOS_INLINE_FUNCTION bool
206 std::size_t idx1) const;
207
209
211
212 [[maybe_unused]] [[nodiscard]] auto get_buffer_index() const;
213
214 template <class Archive> void save(Archive& ar) const;
215
216 template <class Archive> void load(Archive& ar);
217
218 [[nodiscard]] KOKKOS_INLINE_FUNCTION double
219 get_weight(std::size_t idx) const;
220
221 [[nodiscard]] double get_allocation_factor() const;
222
223 [[nodiscard]] std::size_t capacity() const;
224
225 [[nodiscard]] std::size_t get_inactive() const;
226
227 std::size_t update_and_clean_dead(std::size_t out,
228 std::size_t dead,
229 std::size_t threshold);
230
231 void clean_dead(std::size_t to_remove);
232
233 private:
234 static constexpr double default_allocation_factor = 2;
235 Model::SelfParticle buffer_model;
237 Kokkos::View<uint64_t, Kokkos::SharedSpace> buffer_index;
241 std::size_t inactive_counter = 0;
243 void _resize(std::size_t new_size, bool force = false);
245
246 int begin;
247 int end;
248 };
249
250 template <ModelType Model>
252 std::size_t idx, const ContributionView& contributions) const
253 {
254 static_assert(ConstWeightModelType<Model>,
255 "ModelType: Constapply_weight()");
256
257 const double weight = get_weight(idx);
258 const auto pos = position(idx);
259 auto access = contributions.access();
260
261 for (int i = begin; i < end; ++i)
262 {
263 const int rel = i - begin;
264 // const re
265 const double c = weight * model(idx, i);
266 // Kokkos::printf("%d %d %lf\r\n", rel, i, c);
267 access(pos, rel) += c;
268 }
269 // access(pos, 0) += (weight * model(idx, 2)); works
270 }
271
272 template <ModelType Model>
273 [[nodiscard]] KOKKOS_INLINE_FUNCTION std::size_t
278
279 template <ModelType Model>
280 [[nodiscard]] std::size_t ParticlesContainer<Model>::get_inactive() const
281 {
282 return inactive_counter;
283 }
284
285 template <ModelType Model> void ParticlesContainer<Model>::force_remove_dead()
286 {
287 this->clean_dead(this->inactive_counter);
288 this->inactive_counter = 0;
289 }
290
291 template <ModelType Model>
292 [[maybe_unused]] [[nodiscard]] auto
297
298 template <ModelType Model>
300 {
301 return allocation_factor;
302 }
303 template <ModelType Model>
304 [[nodiscard]] std::size_t ParticlesContainer<Model>::capacity() const
305 {
307 }
308
309 template <ModelType Model>
310 template <class Archive>
312 {
314 deserialize_view(ar, weights);
315 deserialize_view(ar, position);
316 deserialize_view(ar, status);
317 deserialize_view(ar, model);
318 deserialize_view(ar, ages);
320 }
321
322 template <ModelType Model>
323 template <class Archive>
324 void ParticlesContainer<Model>::save(Archive& ar) const
325 {
327 serialize_view(ar, weights);
328 serialize_view(ar, position);
329 serialize_view(ar, status);
330 serialize_view(ar, model);
331 serialize_view(ar, ages);
332 }
333
334 template <ModelType Model>
335 std::size_t
337 const std::size_t dead,
338 const std::size_t threshold)
339 {
340 inactive_counter += out;
341 inactive_counter += dead;
342 // TODO: May change threshold as container is now cleaned before exporting,
343 if (inactive_counter > threshold)
344 {
347 }
348 return inactive_counter;
349 }
350
351 template <ModelType Model>
353 const MC::KPRNG::pool_type& random_pool, std::size_t idx1) const
354 {
355 if (Kokkos::atomic_load(&buffer_index()) < buffer_model.extent(0))
356 {
357 const auto idx2 = Kokkos::atomic_fetch_add(&buffer_index(), 1);
358 Model::division(random_pool, idx1, idx2, model, buffer_model);
359 buffer_position(idx2) = position(idx1);
360
361 return true;
362 }
363 Kokkos::printf("%ld %ld\r\n",
364 Kokkos::atomic_load(&buffer_index()),
365 buffer_model.extent(0));
366 return false;
367 }
368
369 template <ModelType Model> void ParticlesContainer<Model>::merge_buffer()
370 {
371 PROFILE_SECTION("ParticlesContainer::merge_buffer")
372 const auto original_size = n_used_elements;
373 const auto n_add_item = buffer_index();
374 if (n_add_item == 0)
375 {
376 return;
377 }
378 _resize(original_size + n_add_item);
379 // Merge position EZ
380
381 Kokkos::parallel_for(
382 "InsertMerge",
383 TeamPolicy(n_add_item, Kokkos::AUTO, Model::n_var),
384 InsertFunctor<Model>(
385 original_size, model, position, buffer_model, buffer_position));
386
387 buffer_index() = 0;
388 n_used_elements += n_add_item;
390 }
391
392 template <ModelType Model>
393 void ParticlesContainer<Model>::_resize(std::size_t new_size, bool force)
394 {
395 PROFILE_SECTION("ParticlesContainer::_resize")
396
397 // Ensure new_size is greater than zero
398 if (new_size > 0)
399 {
400 // Determine if resizing is necessary based on the condition
401 if (new_size > n_allocated_elements || force)
402 {
403 // Calculate the new allocated size
404 const auto new_allocated_size = static_cast<std::size_t>(
405 std::ceil(static_cast<double>(new_size) * allocation_factor));
406
407 // Update the allocated size
408 n_allocated_elements = new_allocated_size;
409
410 // Perform the resizing on all relevant data containers
411 Kokkos::resize(position, n_allocated_elements);
412 Kokkos::resize(model,
414 Model::n_var); // use 2nd dim resize if dynamic
415 Kokkos::resize(status, n_allocated_elements);
416 Kokkos::resize(ages, n_allocated_elements);
417
418 // Handle resizing for weights based on model type
419 if constexpr (ConstWeightModelType<Model>)
420 {
421 Kokkos::resize(weights, 1); // Fixed size for ConstWeightModelType
422 }
423 else
424 {
425 Kokkos::resize(weights, n_allocated_elements);
426 }
427 }
428 }
429 }
430
431 template <ModelType Model>
433 {
434 PROFILE_SECTION("ParticlesContainer::__allocate_buffer__")
435 auto buffer_size = buffer_position.extent(0);
436 if (static_cast<double>(buffer_size) /
437 static_cast<double>(n_allocated_elements) <
439 {
440 buffer_size = static_cast<std::size_t>(
441 std::ceil(static_cast<double>(n_allocated_elements) * buffer_ratio));
442
443 // Realloc because not needed to keep buffer as it has been copied
444 Kokkos::realloc(buffer_position, buffer_size);
445 Kokkos::realloc(buffer_model,
446 buffer_size,
447 Model::n_var); // use 2nd dim resize if dynamic
448 buffer_index() = 0;
449 }
450 }
451
452 template <ModelType M>
454 bool virtual_position)
455 : model(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_model"),
456 0),
457 position(Kokkos::view_alloc(Kokkos::WithoutInitializing,
458 "particle_position"),
459 0),
460 status(
461 Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_status"),
462 0),
463 weights(
464 Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_weigth"),
465 0),
466 ages(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_age"),
467 0),
468 buffer_model("buffer_particle_model", 0),
469 buffer_position("buffer_particle_position", 0),
470 buffer_index("buffer_index"),
472 n_used_elements(n_particle), flag_virtual_position(virtual_position)
473 {
474
475 _resize(n_particle);
477 auto bounds = M::get_bounds();
478 begin = bounds.begin;
479 end = bounds.end;
480 }
481
482 template <ModelType M>
484 : model(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_model"),
485 0),
486 position(Kokkos::view_alloc(Kokkos::WithoutInitializing,
487 "particle_position"),
488 0),
489 status(
490 Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_status"),
491 0),
492 weights(
493 Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_weigth"),
494 0),
495 ages(Kokkos::view_alloc(Kokkos::WithoutInitializing, "particle_age"),
496 0),
497 buffer_model(Kokkos::view_alloc(Kokkos::WithoutInitializing,
498 "buffer_particle_model"),
499 0),
500 buffer_position(Kokkos::view_alloc(Kokkos::WithoutInitializing,
501 "buffer_particle_model"),
502 0),
503 buffer_index("buffer_index"),
506
507 {
508
509 auto bounds = M::get_bounds();
510 begin = bounds.begin;
511 end = bounds.end;
512 }
513
514 template <ModelType M>
515 void ParticlesContainer<M>::clean_dead(std::size_t to_remove)
516 {
517 PROFILE_SECTION("ParticlesContainer::remove_dead")
518 if (to_remove == 0)
519 {
520 return;
521 }
522
523 if (to_remove == n_used_elements)
524 {
525 _resize(0, true);
526 n_used_elements = 0;
527 }
528 else if (to_remove > n_used_elements)
529 {
530 throw std::runtime_error("clean_dead: Error in kernel cannot remove more "
531 "element than existing");
532 }
533 else
534 {
535
536 const auto new_used_item = n_used_elements - to_remove;
537
538 const auto last_used_index = n_used_elements - 1;
539 Kokkos::parallel_scan(
540 "find_and_fill_gap",
541 Kokkos::RangePolicy<ComputeSpace>(0, n_used_elements),
542 FillGapFunctor<M>(
543 status, model, position, ages, to_remove, last_used_index));
544
545 Kokkos::fence();
546 KOKKOS_ASSERT(this->position.extent(0) == n_allocated_elements);
547 KOKKOS_ASSERT(this->model.extent(0) == n_allocated_elements);
548 KOKKOS_ASSERT(this->status.extent(0) == n_allocated_elements);
549 n_used_elements = new_used_item;
550 if (static_cast<double>(n_used_elements) /
551 static_cast<double>(n_allocated_elements) <=
552 0.1)
553 {
554 // std::cout << "SHRINK" << std::endl;
555 _resize(n_used_elements * 2, false);
556 }
557 };
558 }
559
560 // template <ModelType M>
561 // KOKKOS_INLINE_FUNCTION void ParticlesContainer<M>::get_contributions(
562 // std::size_t idx, const ContributionView& contributions) const
563 // {
564 // static_assert(ConstWeightModelType<M>, "ModelType: Const
565 // apply_weight()"); const double weight = get_weight(idx);
566 // M::contribution(idx, position(idx), weight, model, contributions);
567 // }
568
569 // template <ModelType M>
570 // template <typename TeamHandle>
571 // KOKKOS_INLINE_FUNCTION typename std::enable_if<
572 // std::is_same<ExecutionSpace, Kokkos::HostSpace>::value>::type
573 // ParticlesContainer<M>::get_contributions(
574 // const MC::KPRNG::pool_type& random_pool,
575 // std::size_t idx,
576 // const ContributionView& contributions,
577 // const TeamMember& handle) const
578 // {
579
580 // (void)handle;
581 // static_assert(ConstWeightModelType<M>, "ModelType: Constapply_weight()");
582 // const double weight = get_weight(idx);
583 // M::contribution(idx, position(idx), weight, model, contributions);
584
585 // // constexpr int n_virtual_position = 500;
586 // // static_assert(ConstWeightModelType<M>, "ModelType:
587 // Constapply_weight()");
588 // // const double weight = get_weight(idx);
589 // // auto s = random_pool.get_state();
590 // // const auto pos = s.urand(0, n_virtual_position - 1);
591 // // random_pool.free_state(s);
592 // // if (flag_virtual_position)
593 // // {
594 // // Kokkos::View<double*,
595 // // typename ExecutionSpace::scratch_memory_space,
596 // // Kokkos::MemoryTraits<Kokkos::Unmanaged>>
597 // // a(handle.team_scratch(0), n_virtual_position);
598
599 // // const auto mock_contrib = -1 * weight * model(idx, 2);
600 // // Kokkos::atomic_add(&a(pos), mock_contrib);
601 // // handle.team_barrier();
602 // // double sum = 0.;
603 // // parallel_reduce(
604 // // TeamThreadRange(handle, n_virtual_position),
605 // // KOKKOS_LAMBDA(int i, double& lsum) {
606 // // Kokkos::single(PerThread(handle), [=]() { lsum += a(i); });
607 // // },
608 // // sum);
609
610 // // Kokkos::single(Kokkos::PerThread(handle),
611 // // [=]()
612 // // {
613 // // auto access = contributions.access();
614 // // access(0, 0) += sum;
615 // // });
616 // // }
617 // // else
618 // // {
619 // // }
620 // }
621
622 // template <ModelType M>
623 // template <typename ExecutionSpace, typename TeamHandle>
624 // KOKKOS_INLINE_FUNCTION typename std::enable_if<
625 // !std::is_same<ExecutionSpace, Kokkos::HostSpace>::value>::type
626 // ParticlesContainer<M>::get_contributions(
627 // const MC::KPRNG::pool_type& random_pool,
628 // std::size_t idx,
629 // const ContributionView& contributions,
630 // const TeamMember& handle) const
631 // {
632 // (void)handle;
633 // static_assert(ConstWeightModelType<M>, "ModelType: Const
634 // apply_weight()"); const double weight = get_weight(idx);
635 // M::contribution(idx, position(idx), weight, model, contributions);
636 // }
637
638 template <ModelType M>
639 [[nodiscard]] KOKKOS_INLINE_FUNCTION double
641 {
642 if constexpr (ConstWeightModelType<M>)
643 {
644 return weights(0);
645 }
646 else
647 {
648 return weights(idx);
649 }
650 }
651
652} // namespace MC
653
654#endif
Kokkos::Random_XorShift1024_Pool< Kokkos::DefaultExecutionSpace > pool_type
Definition prng.hpp:17
std::size_t get_inactive() const
Definition particles_container.hpp:280
~ParticlesContainer()=default
Default destructor.
void force_remove_dead()
Definition particles_container.hpp:285
Model::SelfParticle model
Definition particles_container.hpp:157
KOKKOS_INLINE_FUNCTION std::size_t n_particles() const
Gets the number of particles in the container.
Definition particles_container.hpp:274
double allocation_factor
Definition particles_container.hpp:238
ParticleWeigths weights
Definition particles_container.hpp:160
int end
Definition particles_container.hpp:247
std::size_t update_and_clean_dead(std::size_t out, std::size_t dead, std::size_t threshold)
Definition particles_container.hpp:336
double get_allocation_factor() const
Definition particles_container.hpp:299
Kokkos::View< uint64_t, Kokkos::SharedSpace > buffer_index
Definition particles_container.hpp:237
void _resize(std::size_t new_size, bool force=false)
Definition particles_container.hpp:393
void __allocate_buffer__()
Definition particles_container.hpp:432
ParticlesContainer()
Definition particles_container.hpp:483
KOKKOS_INLINE_FUNCTION bool handle_division(const MC::KPRNG::pool_type &random_pool, std::size_t idx1) const
Definition particles_container.hpp:352
ParticlesContainer & operator=(ParticlesContainer &&)=default
uint64_t n_used_elements
Definition particles_container.hpp:240
auto get_buffer_index() const
Definition particles_container.hpp:293
void merge_buffer()
Definition particles_container.hpp:369
Model::SelfParticle buffer_model
Definition particles_container.hpp:235
ParticlesContainer(std::size_t n_particle, bool virtual_position=false)
Definition particles_container.hpp:453
KOKKOS_INLINE_FUNCTION double get_weight(std::size_t idx) const
Definition particles_container.hpp:640
KOKKOS_INLINE_FUNCTION void get_contributions(std::size_t idx, const ContributionView &contributions) const
Definition particles_container.hpp:251
std::size_t capacity() const
Definition particles_container.hpp:304
ParticlePositions buffer_position
Definition particles_container.hpp:236
MC::ParticlePositions position
Definition particles_container.hpp:158
void save(Archive &ar) const
Definition particles_container.hpp:324
std::size_t inactive_counter
Definition particles_container.hpp:241
static constexpr double default_allocation_factor
Definition particles_container.hpp:234
static constexpr double buffer_ratio
Definition particles_container.hpp:145
ParticleAges ages
Definition particles_container.hpp:161
Model UsedModel
Alias for the model used by the container.
Definition particles_container.hpp:151
void clean_dead(std::size_t to_remove)
Definition particles_container.hpp:515
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:311
ParticlesContainer(ParticlesContainer &&)=default
std::size_t n_allocated_elements
Definition particles_container.hpp:239
int begin
Definition particles_container.hpp:246
MC::ParticleStatus status
Definition particles_container.hpp:159
bool flag_virtual_position
Definition particles_container.hpp:244
Concept to check if a model type has uniform_weight
Definition traits.hpp:192
Namespace that contains classes and structures related to Monte Carlo (MC) simulations.
Definition alias.hpp:9
Kokkos::View< double *, ComputeSpace > ParticleWeigths
Definition alias.hpp:33
Kokkos::View< Status *, ComputeSpace > ParticleStatus
Definition alias.hpp:32
Kokkos::View< uint64_t *, ComputeSpace > ParticlePositions
Definition alias.hpp:31
Kokkos::Experimental:: ScatterView< double **, Kokkos::LayoutRight > ContributionView
Definition alias.hpp:47
@ Idle
Definition alias.hpp:12
Kokkos::View< double *[2], Kokkos::LayoutLeft, ComputeSpace > ParticleAges
Definition alias.hpp:34