BioCMAMC-ST
move_kernel.hpp
1#ifndef __SIMULATION_MOVE_KERNEL_HPP__
2#define __SIMULATION_MOVE_KERNEL_HPP__
3
4#include "Kokkos_Macros.hpp"
5#include "decl/Kokkos_Declare_OPENMP.hpp"
6#include <Kokkos_Assert.hpp>
7#include <Kokkos_Core.hpp>
8#include <Kokkos_Printf.hpp>
9#include <Kokkos_Random.hpp>
10#include <biocma_cst_config.hpp>
11#include <cassert>
12#include <common/common.hpp>
13#include <mc/alias.hpp>
14#include <mc/domain.hpp>
15#include <mc/events.hpp>
16#include <mc/prng/prng.hpp>
17#include <mc/traits.hpp>
18#include <simulation/probability_leaving.hpp>
19#include <simulation/probe.hpp>
20#include <utility>
21
23{
24 constexpr bool enable_leave = true;
25 constexpr bool disable_leave = false;
26 constexpr bool disable_move = false;
27 constexpr bool enable_move = true;
28
29 struct TagMove
30 {
31 };
32
33 struct TagLeave
34 {
35 };
36
38 {
39 };
40
46 KOKKOS_INLINE_FUNCTION std::size_t
48 const bool do_serch,
51 cumulative_probability,
52 const std::size_t i_compartment,
53 const double random_number)
54 {
55 const int mask_do_serch = static_cast<int>(do_serch);
56 const int max_neighbor = static_cast<int>(neighbors.extent(1));
57
58 KOKKOS_ASSERT(max_neighbor >= 1);
59 KOKKOS_ASSERT(random_number <= 1. && random_number >= 0.);
60 KOKKOS_ASSERT(neighbors.extent(1) == cumulative_probability.extent(1));
61
62 // Do not use cumulative_probability(i_compartment, max_neighbor - 1)==1
63 // Bcause proba can be 0.999999999
64 // KOKKOS_ASSERT(cumulative_probability(i_compartment, max_neighbor -
65 // 1)>0.95); //5% relative
66
67 // v1.1: This assert was there to be sure that the particle will leave but
68 // actually
69 // not needed because if rand< then left will be the last at the the ned of
70 // the loop
71 // KOKKOS_ASSERT(cumulative_probability(i_compartment, max_neighbor - 1)
72 // >= random_number);
73
74 int left = 0;
75 int right = mask_do_serch * (max_neighbor - 1);
76 while (left < right)
77 {
78 const int mid = (left + right) >> 1; // NOLINT
79 const auto pm = cumulative_probability(i_compartment, mid);
80 const int mask = static_cast<int>(random_number > pm);
81 left = mask * (mid + 1) + (1 - mask) * left;
82 right = mask * right + (1 - mask) * mid;
83 }
84 KOKKOS_ASSERT(left >= 0 && static_cast<size_t>(left) < neighbors.extent(1));
85 const auto ret = i_compartment * (1 - mask_do_serch)
86 + neighbors(i_compartment, left) * mask_do_serch;
87 return ret;
88 }
89
90 template <typename ViewType1>
91 KOKKOS_INLINE_FUNCTION void
92 find_flow(const ViewType1& leaving_flow,
93 const std::size_t position,
95 MC::LeavingFlow::float_type& _liquid_volume)
96 {
97 std::size_t i_flow = 0;
98 const std::size_t n_flow = leaving_flow.size();
99 val_flow = 0.;
100 _liquid_volume = 0.;
101 // do-while because n_flow is likely to be 1
102 do
103 {
104 const auto& [index, flow, liquid_volume] = leaving_flow(i_flow++);
105 if (position == index)
106 {
107 val_flow = flow;
108 _liquid_volume = liquid_volume;
109 break;
110 }
111 } while (i_flow < n_flow);
112 }
113
115 {
116 using TeamPolicy = Kokkos::TeamPolicy<ComputeSpace>;
117 using TeamMember = TeamPolicy::member_type;
118 MoveFunctor() = default;
119 MoveFunctor(std::size_t p_team_move,
120 std::size_t p_team_leave,
122 MC::ParticleStatus _status,
124 MC::pool_type _random_pool,
125 MC::EventContainer _events,
127 MC::ParticleAges _ages)
128 : MoveFunctor(p_team_move,
129 p_team_leave,
130 0,
131 std::move(p),
132 std::move(_status),
133 0,
134 std::move(m),
135 std::move(_random_pool),
136 std::move(_events),
137 std::move(_probes),
138 std::move(_ages),
139 false,
140 false) {};
141
142 MoveFunctor(std::size_t p_team_move,
143 std::size_t p_team_leave,
144 double _d_t,
146 MC::ParticleStatus _status,
147 std::size_t n_p,
149 MC::pool_type _random_pool,
150 MC::EventContainer _events,
152 MC::ParticleAges _ages,
153 bool b_move,
154 bool b_leave)
155 : d_t(_d_t), positions(std::move(p)), n_particles(n_p),
156 move(std::move(m)), random_pool(_random_pool), // NOLINT
157 status(std::move(_status)), events(std::move(_events)),
158 probes(std::move(_probes)), ages(std::move(_ages)),
159 m_p_team_leave(p_team_leave), m_p_team_move(p_team_move),
160 m_enable_move(b_move), m_enable_leave(b_leave) {};
161
162 void
163 update(double _d_t,
164 std::size_t n_p,
166 MC::ParticlePositions _positions,
167 MC::ParticleStatus _status,
168 MC::ParticleAges _ages,
169 bool b_move,
170 bool b_leave)
171 {
172
173 this->d_t = _d_t;
174 this->n_particles = n_p;
175 m_enable_leave = b_leave;
176 m_enable_move = b_move;
177 this->move = std::move(move_i);
178 this->positions = std::move(_positions);
179 this->status = std::move(_status);
180 this->ages = std::move(_ages);
181 }
182
183 [[nodiscard]] bool
185 {
187 }
188
189 KOKKOS_INLINE_FUNCTION void
191 const Kokkos::TeamPolicy<ComputeSpace>::member_type& team) const
192 {
193 using ScratchSpace
194 = Kokkos::TeamPolicy<>::execution_space::scratch_memory_space;
195 using ScratchView = Kokkos::View<float*, ScratchSpace>;
196
197 const std::size_t count = m_p_team_move;
198 const std::size_t p0 = team.league_rank() * count;
199 const std::size_t n_particle = n_particles;
200
201 const auto upper_bound
202 = ((p0 + count) >= n_particle) ? n_particle - p0 : count;
203 KOKKOS_ASSERT(upper_bound > 0 && upper_bound < n_particle);
204 const auto& rp = random_pool;
205
206 const std::size_t N = count * 2;
207
208 // rng is actually a flat m*p array
209 const std::size_t p = team.team_size();
210 const std::size_t m = (N + p - 1) / p;
211
212 ScratchView rng(team.team_scratch(0), N);
213
214 // Use "tiling" to minimize contention when aquired_state
215 // State is aquired m times instead of N, it is supposed to reduce
216 // contention
217
218 Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, p),
219 [&rp, &rng, N, m, p](const std::size_t idx)
220 {
221 // Ok to use here, get_state should be called in
222 // each thread
223 auto gen = rp.get_state();
224
225 // current thread iteration p times with the same
226 // state
227 for (std::size_t k = 0; k < m; ++k)
228 {
229 const std::size_t i = idx + k * p; // stride p
230
231 if (i >= N)
232 {
233 break;
234 }
235
236 rng(i) = gen.frand(0., 1.);
237 }
238
239 rp.free_state(gen);
240 });
241 team.team_barrier();
242
243 // We can use flat array index here ordering of random doesnt matter
244 Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, upper_bound),
245 [&](const std::size_t idx)
246 {
247 const auto flat_index = p0 + idx;
248
249 if (status(flat_index) == MC::Status::Idle)
250 {
251
252 const std::size_t base = idx * 2;
253 KOKKOS_ASSERT(base + 1 < N);
254 const auto rng1 = rng(base);
255 const auto rng2 = rng(base + 1);
256 handle_move(flat_index, rng1, rng2);
257 }
258 });
259 }
260
261 KOKKOS_INLINE_FUNCTION void
263 const Kokkos::TeamPolicy<ComputeSpace>::member_type& team_handle,
264 std::size_t& local_dead_count) const
265 {
266 (void)_tag;
267 const std::size_t count = m_p_team_leave;
268 const std::size_t p0 = team_handle.league_rank() * count;
269 const std::size_t n_particle = n_particles;
270
271 const auto upper_bound
272 = ((p0 + count) >= n_particle) ? n_particle - p0 : count;
273
274 KOKKOS_ASSERT(upper_bound > 0 && upper_bound <= n_particle);
275 std::size_t team_dead_count = 0;
276 const auto& lf = move.leaving_flow;
277 Kokkos::parallel_reduce(
278 Kokkos::TeamThreadRange(team_handle, 0, upper_bound),
279 [&](const std::size_t idx, std::size_t& thread_dead_count)
280 {
281 const auto flat_index = p0 + idx;
282 // ages(flat_index, 0) += d_t;
283 handle_exit(flat_index, lf, thread_dead_count);
284 },
285 team_dead_count);
286
287 team_handle.team_barrier();
288 Kokkos::single(
289 Kokkos::PerTeam(team_handle),
290 [&]()
291 {
292 if constexpr (AutoGenerated::FlagCompileTime::enable_event_counter)
293 {
294 events.add<MC::EventType::Exit>(team_dead_count);
295 }
296 local_dead_count += team_dead_count;
297 });
298 }
299
300 bool
301 do_move() const
302 {
303 return this->m_enable_move;
304 }
305
306 bool
307 do_leave() const
308 {
309 return m_enable_leave;
310 }
311
312 KOKKOS_INLINE_FUNCTION void
314 const Kokkos::TeamPolicy<ComputeSpace>::member_type& team_handle,
315 std::size_t& local_dead_count) const
316 {
317
318 (void)_tag;
319
320 const auto& lf = move.leaving_flow;
321
322 // In 0d, the flow is the same for every particle
323 // Only one member of the team calculate lambda and
324 // broadcast it to other members
325 double lambda = 0.;
326 Kokkos::single(
327 Kokkos::PerTeam(team_handle),
328 [&lf, dt = this->d_t](double& local_lambda)
329 {
330 const auto& [_, flow_value, liquid_volume] = lf(0);
331
332 local_lambda = dt * flow_value / liquid_volume;
333 },
334 lambda);
335
336 team_handle.team_barrier();
337 // In 0d context we can even be sure that flow will never be 0
338 // Note: if flow is 0, proba will be 0 and then the result will be the
339 // same Can we remove the condition?
340 //
341 if (lambda == 0.)
342 {
343 return;
344 }
345 const std::size_t N = m_p_team_leave;
346 const std::size_t p0 = team_handle.league_rank() * N;
347 const auto upper_bound = ((p0 + N) >= n_particles) ? n_particles - p0 : N;
348
349 KOKKOS_ASSERT(upper_bound > 0 && upper_bound <= n_particles);
350
351 const auto& rp = random_pool;
352
353 const std::size_t p = team_handle.team_size();
354
355 const std::size_t m = (upper_bound + p - 1) / p;
356
357 std::size_t team_dead_count = 0;
358
359 // For each thread of the team we assigm m iterations
360 // This divide the number of call of get_state by m and may reduce
361 // contention especially on CPU typically P = 1 on cpu then m is
362 // equal to npt (128-4096)
363 Kokkos::parallel_reduce(
364 Kokkos::TeamThreadRange(team_handle, 0, p),
365 [&](const std::size_t tid, std::size_t& dead_count)
366 {
367 auto gen = rp.get_state();
368
369 for (std::size_t k = 0; k < m; ++k)
370 {
371 const std::size_t idx = tid + k * p; // stride p
372
373 if (idx >= upper_bound)
374 {
375 break;
376 }
377 const std::size_t flat_index = p0 + idx;
378
379 if (status(flat_index) != MC::Status::Idle)
380 {
381 continue;
382 }
383 const double r = gen.drand(0., 1.);
384
386 probability_leaving<decltype(r), precision_tag>(r, lambda),
387 flat_index,
388 dead_count);
389 }
390 rp.free_state(gen);
391 },
392 team_dead_count);
393
394 team_handle.team_barrier();
395
396 Kokkos::single(
397 Kokkos::PerTeam(team_handle),
398 [&]()
399 {
400 if constexpr (AutoGenerated::FlagCompileTime::enable_event_counter)
401 {
402 events.add<MC::EventType::Exit>(team_dead_count);
403 }
404 local_dead_count += team_dead_count;
405 });
406 }
407
408 KOKKOS_FUNCTION void
409 handle_move(const std::size_t idx, const float rng1, const float rng2) const
410 {
411
412 // const auto rng1 = static_cast<float>(random(idx, 0));
413 // const auto rng2 = static_cast<float>(random(idx, 1));
414
415 KOKKOS_ASSERT(rng1 >= 0. && rng1 <= 1 && rng2 >= 0. && rng2 <= 1);
416
417 const std::size_t i_current_compartment = positions(idx);
418
419 KOKKOS_ASSERT(
420 i_current_compartment < move.liquid_volume.extent(0)
421 && "Particle position is incorect (greater than compartment number)");
422
423 const bool mask_next = probability_leaving<float, fast_tag>(
424 rng1,
425 move.liquid_volume(i_current_compartment),
426 move.diag_transition(i_current_compartment),
427 d_t);
428
429 positions(idx) = __find_next_compartment(mask_next,
430 move.neighbors,
431 move.cumulative_probability,
432 i_current_compartment,
433 rng2);
434
435 // positions(idx)
436 // = (mask_next) ? __find_next_compartment(move.neighbors,
437 // move.cumulative_probability,
438 // i_current_compartment,
439 // rng2)
440 // : i_current_compartment;
441
442 KOKKOS_ASSERT(
443 positions(idx) < move.liquid_volume.extent(0)
444 && " Position after move is greater than compartment number");
445
446 if constexpr (AutoGenerated::FlagCompileTime::enable_event_counter)
447 {
448 if (mask_next)
449 {
450 events.wrap_incr<MC::EventType::Move>();
451 }
452 }
453 }
454
455 template <typename ExecSpace>
456 KOKKOS_FORCEINLINE_FUNCTION std::size_t
458 const std::size_t idx,
459 const Kokkos::View<const MC::LeavingFlow*, ExecSpace>& leaving_flow,
460 std::size_t& dead_count) const
461 {
462
463 const std::size_t position = positions(idx);
464 // Strategy:
465 // first find the value of leaving flow (0-> particle doesn´t leave)
466 // do-while +early break is ok as n_flow is likely <10
467 // second: calculate probability leaving, flow=0 => p=0 theres no need
468 // to check condition
469 MC::LeavingFlow::float_type found_flow_value = 0.;
470 MC::LeavingFlow::float_type found_liquid_volume = 0.;
471 find_flow(leaving_flow, position, found_flow_value, found_liquid_volume);
472
473 // Cases
474 // only for few particles
475 if (found_flow_value != 0.)
476 {
477
478 auto gen = random_pool.get_state();
479 const auto r = gen.frand(0., 1.);
480 random_pool.free_state(gen);
481
482 KOKKOS_ASSERT(found_liquid_volume > 0.);
483 KOKKOS_ASSERT(found_flow_value > 0.);
485 r, found_liquid_volume, found_flow_value, d_t);
486 perform_exit(p, idx, dead_count);
487 }
488
489 return 0;
490 }
491
492 KOKKOS_INLINE_FUNCTION void
493 perform_exit(const bool proba,
494 const std::size_t idx,
495 std::size_t& dead_count) const
496 {
497
498 if (proba)
499 {
500 ++dead_count;
501
502 if constexpr (AutoGenerated::FlagCompileTime::use_probe)
503 {
504 using mem_space = ComputeSpace::memory_space;
505 // DO this betore age is reset to 0
506 const auto _ = probes.set<mem_space>(ages(idx, 0));
507 }
508
509 ages(idx, 0) = 0;
511 }
512 else
513 {
514 ages(idx, 0) += d_t;
515 }
516 }
517
518 double d_t{};
520 std::size_t n_particles{};
527 std::size_t m_p_team_leave{};
528 std::size_t m_p_team_move{};
529
532 };
533} // namespace Simulation::KernelInline
534
535#endif
std::conditional_t< is_const, Kokkos::View< const std::size_t **, Kokkos::LayoutRight, ExecSpace, Kokkos::MemoryTraits< Kokkos::RandomAccess > >, Kokkos::View< std::size_t **, Kokkos::LayoutRight, ExecSpace > > NeighborsView
Definition alias.hpp:206
Kokkos::View< Status *, ComputeSpace > ParticleStatus
Definition alias.hpp:141
Kokkos::View< uint64_t *, ComputeSpace > ParticlePositions
Definition alias.hpp:140
@ Move
Move in domain.
Definition events.hpp:22
@ Exit
Remove particle from list due to move in domain.
Definition events.hpp:21
@ Idle
Definition alias.hpp:126
@ Exit
Definition alias.hpp:128
ParticleAgesBase< ComputeSpace > ParticleAges
Definition alias.hpp:144
gen_pool_type< Kokkos::DefaultExecutionSpace > pool_type
Definition alias.hpp:100
std::conditional_t< is_const, Kokkos::View< const double **, Kokkos::LayoutRight, ExecSpace, Kokkos::MemoryTraits< Kokkos::RandomAccess > >, Kokkos::View< double **, Kokkos::LayoutRight, ExecSpace > > CumulativeProbabilityView
Definition alias.hpp:197
Definition kernels.hpp:19
constexpr bool enable_leave
Definition move_kernel.hpp:24
constexpr bool enable_move
Definition move_kernel.hpp:27
KOKKOS_INLINE_FUNCTION std::size_t __find_next_compartment(const bool do_serch, const MC::NeighborsView< ComputeSpace, true > &neighbors, const MC::CumulativeProbabilityView< ComputeSpace, true > &cumulative_probability, const std::size_t i_compartment, const double random_number)
probably overkill binary search to find next compartment
Definition move_kernel.hpp:47
constexpr bool disable_leave
Definition move_kernel.hpp:25
KOKKOS_INLINE_FUNCTION bool probability_leaving(const T random_number, const double lambda)
Definition probability_leaving.hpp:26
KOKKOS_INLINE_FUNCTION void find_flow(const ViewType1 &leaving_flow, const std::size_t position, MC::LeavingFlow::float_type &val_flow, MC::LeavingFlow::float_type &_liquid_volume)
Definition move_kernel.hpp:92
int precision_tag
Definition probability_leaving.hpp:12
KOKKOS_INLINE_FUNCTION bool probability_leaving< float, fast_tag >(float random_number, double volume, double flow, double dt)
Definition probability_leaving.hpp:47
constexpr bool disable_move
Definition move_kernel.hpp:26
Probes< AutoGenerated::probe_buffer_size > ProbeAutogeneratedBuffer
Definition probe.hpp:150
Structure to store information about domain needed during MC cycle data is likely to change between e...
Definition domain.hpp:29
Use to count events that occurs during Monte-Carlo processing cycles.
Definition events.hpp:151
double float_type
Definition domain.hpp:19
MoveFunctor(std::size_t p_team_move, std::size_t p_team_leave, double _d_t, MC::ParticlePositions p, MC::ParticleStatus _status, std::size_t n_p, MC::DomainState< ComputeSpace > m, MC::pool_type _random_pool, MC::EventContainer _events, ProbeAutogeneratedBuffer _probes, MC::ParticleAges _ages, bool b_move, bool b_leave)
Definition move_kernel.hpp:142
KOKKOS_FUNCTION void handle_move(const std::size_t idx, const float rng1, const float rng2) const
Definition move_kernel.hpp:409
bool do_move() const
Definition move_kernel.hpp:301
bool m_enable_leave
Definition move_kernel.hpp:531
MoveFunctor(std::size_t p_team_move, std::size_t p_team_leave, MC::ParticlePositions p, MC::ParticleStatus _status, MC::DomainState< ComputeSpace > m, MC::pool_type _random_pool, MC::EventContainer _events, ProbeAutogeneratedBuffer _probes, MC::ParticleAges _ages)
Definition move_kernel.hpp:119
KOKKOS_INLINE_FUNCTION void operator()(TagLeaveB0D _tag, const Kokkos::TeamPolicy< ComputeSpace >::member_type &team_handle, std::size_t &local_dead_count) const
Definition move_kernel.hpp:313
KOKKOS_INLINE_FUNCTION void operator()(TagLeave _tag, const Kokkos::TeamPolicy< ComputeSpace >::member_type &team_handle, std::size_t &local_dead_count) const
Definition move_kernel.hpp:262
bool need_launch() const
Definition move_kernel.hpp:184
Kokkos::TeamPolicy< ComputeSpace > TeamPolicy
Definition move_kernel.hpp:116
TeamPolicy::member_type TeamMember
Definition move_kernel.hpp:117
MC::ParticlePositions positions
Definition move_kernel.hpp:519
std::size_t m_p_team_move
Definition move_kernel.hpp:528
double d_t
Definition move_kernel.hpp:518
MC::DomainState< ComputeSpace, true > move
Definition move_kernel.hpp:521
KOKKOS_FORCEINLINE_FUNCTION std::size_t handle_exit(const std::size_t idx, const Kokkos::View< const MC::LeavingFlow *, ExecSpace > &leaving_flow, std::size_t &dead_count) const
Definition move_kernel.hpp:457
std::size_t n_particles
Definition move_kernel.hpp:520
MC::EventContainer events
Definition move_kernel.hpp:524
ProbeAutogeneratedBuffer probes
Definition move_kernel.hpp:525
KOKKOS_INLINE_FUNCTION void perform_exit(const bool proba, const std::size_t idx, std::size_t &dead_count) const
Definition move_kernel.hpp:493
void update(double _d_t, std::size_t n_p, MC::DomainState< ComputeSpace > move_i, MC::ParticlePositions _positions, MC::ParticleStatus _status, MC::ParticleAges _ages, bool b_move, bool b_leave)
Definition move_kernel.hpp:163
MC::ParticleStatus status
Definition move_kernel.hpp:523
KOKKOS_INLINE_FUNCTION void operator()(TagMove, const Kokkos::TeamPolicy< ComputeSpace >::member_type &team) const
Definition move_kernel.hpp:190
MC::ParticleAges ages
Definition move_kernel.hpp:526
bool m_enable_move
Definition move_kernel.hpp:530
std::size_t m_p_team_leave
Definition move_kernel.hpp:527
MC::pool_type random_pool
Definition move_kernel.hpp:522
bool do_leave() const
Definition move_kernel.hpp:307
Definition move_kernel.hpp:38
Definition move_kernel.hpp:34
Definition move_kernel.hpp:30