Peano
Loading...
Searching...
No Matches
TaskGraph.py
Go to the documentation of this file.
1# This file is part of the ExaParty project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3from exaparty.api.particle.AlgorithmStep import AlgorithmStep
4from exaparty.api.actionsets.UpdateParticleMarker import UpdateParticleMarker
5
6import peano4.api
7import exaparty
8
9
10from .AMRLogic import add_dynamic_mesh_refinement_and_particle_resorting
11from .ParticleSortingAndStorage import (
12 ParticleSortingAndStorage,
13 AddAdditionalDummySweep,
14)
15from .SpeciesStepOrdering import concatenate_steps_of_species
16from .Utils import (
17 does_one_species_require_gather,
18 get_species_set,
19 get_observer_prefix,
20 StepType,
21)
22from .Sequential import (
23 map_onto_separate_mesh_traversals,
24 map_onto_separate_mesh_traversals_with_generic_mapper,
25)
26
27
29 step,
30 current_species_set,
31 mesh_traversals,
32 alter_particle_position,
33 use_continuous_particles_sequences,
34 step_type: StepType,
35 verbose,
36 synchronise_task_graph_after_each_mesh_sweep=True,
37):
38 """!
39
40 Translate one step of species into mesh traversal
41
42 We create a new step and then add various different action sets depending
43 on the context and behaviour. Once befilled, the new step is appended to
44 mesh_traversals.
45
46 1. If this is an initialisation sequence, then I clear all the task
47 markers. This is to ensure that they do not hold any garbage. I can
48 clear the set as often as I want. It is just important that I clear
49 it at least once per particle species.
50 2. Update the task markers. This is something each and every step in Peano
51 does. As we inject the update into each and every action set, we become
52 independent of the AMR and when it is actually realised.
53 3. Pick computational kernel. This is where the actual computation per step
54 is done. We have two fundamentally different kernels: one keeps track of
55 (multiscale) neighbours, while the other just updates the particles
56 within the octant (cell).
57 4. If an action set alters the position or cut-off of a particle, then we
58 append True to alter_particle_position. Otherwise, we append False.
59 This is decided from step.effect, i.e. whether it is one of the
60 CHANGE_POSITION_OR_INTERACTION_RADIUS(_AND_MIGHT_RERUN) variants.
61 @see AlgorithmStep.may_trigger_rerun()
62
63 Here's where the action set differs completely from the Sequential graph
64 compiler. If we change a position, we erase all the task indices from
65 the tree.
66
67 In general, we never assign any index to touchCellLastTime(), as ExaParty does
68 not only one type of plug-in points. This is actually touchCellFirstTime()
69 in the realisation, so one might be tempted to mask out touchCellLastTime()
70 and only use task indices for touchCellFirstTime(). However, we have to
71 plug into touchVertexLastTime(), too, and this one has to know about events
72 within the adjacent cells. The action set AssignTaskNumbersToMesh memorises
73 the touchCellLastTime() indices within the vertices, to we use the numbers
74 from touchCellLastTime() but actually plug into touchCellFirstTime().
75
76
77 @param current_species_set: Particle
78 You get a specific algorithmic step as input. The current_species_set
79 highlights to which this step originally belonged to.
80
81 @param mesh_traversals: [peano4.api.solversteps.Step]
82 Inout parameter. We append a new step to this sequence.
83
84 @param alter_particle_position: [Bool]
85 Inout parameter. We append a new marker to this sequence.
86
87 @param synchronise_task_graph_after_each_mesh_sweep: Bool
88 If set, the arising action set ill call a tarch::multicore::waitForAllTasks()
89 after each mesh traversal.
90
91
92
93 ## Use it to construct task graphs
94
95 The routine is not expected to have an argument synchronise_task_graph_after_each_mesh_sweep
96 in Sequential. But I want to use the routines from Sequential. So I give
97 this value a default value. This way, I can reuse the routine in TaskGraph,
98 where I simply redefine the default.
99
100 """
102 "{}{}{}_sweep{}".format(
103 get_observer_prefix(step_type),
104 current_species_set.particle_model.name,
105 step.name,
106 len(mesh_traversals),
107 ),
108 add_user_defined_actions=False,
109 )
110
111 action_set_update_marker = UpdateParticleMarker(
112 particle_set=current_species_set,
113 automatically_set_marker_in_leave_cell=False,
114 make_reset_separate_task=True,
115 )
116 action_set_update_marker.descend_invocation_order = (
117 new_step.highest_descend_invocation_order() + 1
118 )
119 action_set_update_marker.parallel = True
120 new_step.add_action_set(action_set_update_marker)
121 if verbose:
122 print(
123 "GRAPH COMPILER DEBUG [translate_one_step_of_species_into_mesh_traversal]: step no {} ({}) on particle {} uses UpdateParticleMarker".format(
124 len(mesh_traversals),
125 step.name,
126 current_species_set.particle_model.name,
127 )
128 )
129
130 #
131 # Need to know this for the action sets later down the line, as they have
132 # to know if the tasks have to be waited for immediately.
133 #
134 current_step_alters_particle_position = (
135 step.effect
136 == AlgorithmStep.Effect.CHANGE_POSITION_OR_INTERACTION_RADIUS_AND_MIGHT_RERUN
137 or step.effect == AlgorithmStep.Effect.CHANGE_POSITION_OR_INTERACTION_RADIUS
138 )
139
140 #
141 # We have to synchronise after each traversal if we work with a
142 # tree or alter the global state
143 #
144 synchronise_after_each_mesh_traversal = (
145 step.effect == AlgorithmStep.Effect.ALTER_GLOBAL_STATE
146 or step.effect == AlgorithmStep.Effect.ALTER_GLOBAL_STATE_AND_MIGHT_RERUN
147 or step.effect == AlgorithmStep.Effect.ALTER_LOCAL_STATE_AND_MIGHT_RERUN
148 or step.effect == AlgorithmStep.Effect.CHANGE_POSITION_OR_INTERACTION_RADIUS
149 or step.effect == AlgorithmStep.Effect.CHANGE_POSITION_OR_INTERACTION_RADIUS_AND_MIGHT_RERUN
150 or synchronise_task_graph_after_each_mesh_sweep
151 )
152
153 #
154 # Pick computational kernel
155 # =========================
156 # This is where the actual computation per step is done. We have two
157 # fundamentally different kernels: one keeps track of (multiscale)
158 # neighbours, while the other just updates the particles within the
159 # octant (cell).
160 #
161 if step.dependencies == AlgorithmStep.Dependencies.NEIGHBOURS:
162 if use_continuous_particles_sequences:
163 if verbose:
164 print(
165 "GRAPH COMPILER DEBUG [translate_one_step_of_species_into_mesh_traversal]: step no {} ({}) on particle {} can use the peano4.api.toolbox.particles.UpdateParticle_MultiLevelInteraction_StackOfLists_ContiguousParticles action set and hence optimise aggressively".format(
166 len(mesh_traversals),
167 step.name,
168 current_species_set.particle_model.name,
169 )
170 )
172 particle_set=current_species_set,
173 particle_particle_interaction_kernel=step.cell_kernel,
174 touch_vertex_first_time_compute_particle_update_kernel=step.touch_vertex_first_time_kernel,
175 touch_vertex_last_time_compute_particle_update_kernel=step.touch_vertex_last_time_kernel,
176 prepare_traversal_kernel=step.prepare_traversal_kernel,
177 unprepare_traversal_kernel=step.unprepare_traversal_kernel,
178 additional_includes=step.includes,
179 active_particle_set=step.input_particles,
180 alter_particle_position_or_interaction_radius=current_step_alters_particle_position,
181 wait_for_all_tasks_to_finish_after_traversal=synchronise_after_each_mesh_traversal,
182 )
183 else:
184 if verbose:
185 print(
186 "GRAPH COMPILER DEBUG [translate_one_step_of_species_into_mesh_traversal]: step no {} ({}) on particle {} uses the peano4.api.toolbox.particles.UpdateParticle_MultiLevelInteraction_Sets".format(
187 len(mesh_traversals),
188 step.name,
189 current_species_set.particle_model.name,
190 )
191 )
192 action_set_particle_particle_interaction = exaparty.api.actionsets.UpdateParticle_MultiLevelInteraction_Sets(
193 particle_set=current_species_set,
194 particle_particle_interaction_kernel=step.cell_kernel,
195 touch_vertex_first_time_compute_particle_update_kernel=step.touch_vertex_first_time_kernel,
196 touch_vertex_last_time_compute_particle_update_kernel=step.touch_vertex_last_time_kernel,
197 prepare_traversal_kernel=step.prepare_traversal_kernel,
198 unprepare_traversal_kernel=step.unprepare_traversal_kernel,
199 additional_includes=step.includes,
200 active_particle_set=step.input_particles,
201 alter_particle_position_or_interaction_radius=current_step_alters_particle_position,
202 wait_for_all_tasks_to_finish_after_traversal=synchronise_after_each_mesh_traversal,
203 )
204 action_set_particle_particle_interaction.descend_invocation_order = (
205 new_step.highest_descend_invocation_order() + 1
206 )
207 action_set_particle_particle_interaction.parallel = True
208 new_step.add_action_set(action_set_particle_particle_interaction)
209 elif step.dependencies == AlgorithmStep.Dependencies.SELF:
210 if use_continuous_particles_sequences:
211 if verbose:
212 print(
213 "GRAPH COMPILER DEBUG [translate_one_step_of_species_into_mesh_traversal]: step no {} ({}) on particle {} uses peano4.api.toolbox.particles.UpdateParticle_SingleLevelInteraction_ContiguousParticles action set".format(
214 len(mesh_traversals),
215 step.name,
216 current_species_set.particle_model.name,
217 )
218 )
219
221 particle_set=current_species_set,
222 particle_cell_update_kernel=step.cell_kernel,
223 touch_vertex_first_time_compute_particle_update_kernel=step.touch_vertex_first_time_kernel,
224 touch_vertex_last_time_compute_particle_update_kernel=step.touch_vertex_last_time_kernel,
225 prepare_traversal_kernel=step.prepare_traversal_kernel,
226 unprepare_traversal_kernel=step.unprepare_traversal_kernel,
227 additional_includes=step.includes,
228 active_particle_set=step.input_particles,
229 alter_particle_position_or_interaction_radius=current_step_alters_particle_position,
230 wait_for_all_tasks_to_finish_after_traversal=synchronise_after_each_mesh_traversal,
231 )
232 action_set_particle_update.descend_invocation_order = (
233 new_step.highest_descend_invocation_order() + 1
234 )
235 action_set_particle_update.parallel = True
236 new_step.add_action_set(action_set_particle_update)
237 else:
238 if verbose:
239 print(
240 "GRAPH COMPILER DEBUG [translate_one_step_of_species_into_mesh_traversal]: step no {} ({}) on particle {} uses peano4.api.toolbox.particles.UpdateParticle_SingleLevelInteraction action set".format(
241 len(mesh_traversals),
242 step.name,
243 current_species_set.particle_model.name,
244 )
245 )
246
248 particle_set=current_species_set,
249 particle_cell_update_kernel=step.cell_kernel,
250 touch_vertex_first_time_compute_particle_update_kernel=step.touch_vertex_first_time_kernel,
251 touch_vertex_last_time_compute_particle_update_kernel=step.touch_vertex_last_time_kernel,
252 prepare_traversal_kernel=step.prepare_traversal_kernel,
253 unprepare_traversal_kernel=step.unprepare_traversal_kernel,
254 additional_includes=step.includes,
255 active_particle_set=step.input_particles,
256 alter_particle_position_or_interaction_radius=current_step_alters_particle_position,
257 wait_for_all_tasks_to_finish_after_traversal=synchronise_after_each_mesh_traversal,
258 )
259 action_set_particle_update.descend_invocation_order = (
260 new_step.highest_descend_invocation_order() + 1
261 )
262 action_set_particle_update.parallel = True
263 new_step.add_action_set(action_set_particle_update)
264 else:
265 assert False, "step {} seems to have no action at all".format(step)
266
267 #
268 # As we have a 1:1 mapping, we can directly translate the effect into flags
269 #
270 if current_step_alters_particle_position:
271 alter_particle_position.append(True)
272 else:
273 alter_particle_position.append(False)
274
275 #
276 # Now add the task number management. First step erases all indices, as it
277 # might be the first time stepping step overall. Other than that, all steps
278 # plug into creational and destroy events.
279 #
280 action_set_assign_task_markers = peano4.api.toolbox.api.AssignNumbersToMesh(
281 # current_species_set.particle_model.name,
282 current_species_set.name,
283 )
284 action_set_assign_task_markers.descend_invocation_order = (
285 new_step.lowest_descend_invocation_order() - 1
286 )
287 action_set_assign_task_markers.parallel = True
288 new_step.add_action_set(action_set_assign_task_markers)
289 if verbose:
290 print(
291 "GRAPH COMPILER DEBUG [translate_one_step_of_species_into_mesh_traversal]: step no {} ({}) on particle {} is added peano4.api.toolbox.api.AssignNumbersToMesh".format(
292 len(mesh_traversals),
293 step.name,
294 current_species_set.particle_model.name,
295 )
296 )
297
298 mesh_traversals.append(new_step)
299
300
302 sequence_of_steps,
303 particle_sorting_and_storage: ParticleSortingAndStorage,
304 step_type: StepType,
305 verbose,
306):
307 """!
308
309 Simpest graph compiler constructing a task tree
310
311 This graph compiler is very very similar to Sequential.map_onto_separate_mesh_traversals().
312 It runs over the tree and generates one task per touchVertexFirstTime(),
313 touchCellFirstTime() and touchVertexLastTime(). We end up with a
314 task graph that resembles an overlay of a task tree and the graph
315 belonging to a Cartesian mesh with a vertex-cell-vertex update
316 sequence.
317
318 @image html documentation/exaparty/task-graph-tree.png
319
320 Whenever we spawn a task, we first generate a new task id. This is the
321 number of this task which we store within the task and which the task
322 has to free eventually. We also store this task within the associated
323 mesh entity (vertex or cell) and hence can use the tree as a lookup
324 mechanism of the task dependencies. That is, the indices within the
325 tree are exclusively used for lookups and not for any semantics.
326
327 ## Realisation
328
329 This routine is actually a pure forward delegation. The actual tasking is
330 injected by passing in translate_one_step_of_species_into_mesh_traversal()
331 as functor. Therefore, this routine holds more information on the actual
332 task semantics.
333
334 @param sequence_of_steps: [(Species,Step)]
335 Sequence of tuples each consisting of one species and one step. Typically
336 constructed through functions within SpeciesStepOrdering.py.
337
338 @param particle_sorting: ParticleSorting
339
340 @param observer_preamble: String
341 Usually people pass in "Init" or "Step" here to distinguish observers for
342 the initialisation from the actual particle update steps.
343
344 @param verbose: {True, False}
345
346 @return mesh_traversals
347
348 """
349 return map_onto_separate_mesh_traversals_with_generic_mapper(
350 sequence_of_steps,
351 particle_sorting_and_storage,
352 step_type,
353 verbose,
354 translate_one_step_of_species_into_mesh_traversal,
355 )
356
357
358def add_clear_task_number_to_very_first_initialisation_step(
359 algorithm_steps, species_sets, verbose
360):
361 """!
362
363 Take set of mesh run throughs (algorithm steps) and add clear task number action set
364
365 Usually called on initialisation routines.
366
367 """
368
369 for current_species_set in species_sets:
370 action_set_erase_task_markers = peano4.api.toolbox.api.ClearNumbersOnMesh(
371 current_species_set.name,
372 )
373 action_set_erase_task_markers.descend_invocation_order = (
374 algorithm_steps[0].lowest_descend_invocation_order() - 1
375 )
376 action_set_erase_task_markers.parallel = True
377 algorithm_steps[0].add_action_set(action_set_erase_task_markers)
378 if verbose:
379 print(
380 "GRAPH COMPILER DEBUG [add_clear_task_number_to_very_first_initialisation_step]: step no {} on particle {} clears task numbers/markers and hence uses the peano4.api.toolbox.api.ClearNumbersOnMesh action set clearing all task numbers and assigning new ones".format(
381 algorithm_steps[0].name,
382 current_species_set.particle_model.name,
383 )
384 )
385
386
387def initialisation_steps_onto_task_graph_multiscale_sort_scattered_memory(
388 species_sets,
389 verbose,
390):
391 """!
392
393 @see map_onto_separate_mesh_traversals()
394
395 @param concatenate_steps_of_species: [Species]
396 Sequence of particle species to be handled.
397
398 @param verbose: {True, False}
399
400 """
402 sequence_of_steps=concatenate_steps_of_species(species_sets, True),
403 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_WITH_LIFTS_AND_DROPS_SCATTERED_PARTICLE_STORAGE,
404 step_type=StepType.INITIALISATION,
405 verbose=verbose,
406 )
407 add_clear_task_number_to_very_first_initialisation_step(
408 result, species_sets, verbose
409 )
410 return result
411
412
413# ====================================
414# Public API functions
415# ====================================
416#
417# These functions are made available through the __init__.py file. They are all
418# "just" wrappers around the generic map_onto_separate_mesh_traversals()
419# function calls, yet with different arguments.
420#
421def initialisation_steps_onto_task_graph_bucket_sort_scattered_memory(
422 species_sets,
423 verbose,
424):
425 """!
426
427 @see map_onto_separate_mesh_traversals()
428
429 @param concatenate_steps_of_species: [Species]
430 Sequence of particle species to be handled.
431
432 @param verbose: {True, False}
433
434 """
436 sequence_of_steps=concatenate_steps_of_species(species_sets, True),
437 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_ON_THE_FLY_SCATTERED_PARTICLE_STORAGE,
438 step_type=StepType.INITIALISATION,
439 verbose=verbose,
440 )
441 add_clear_task_number_to_very_first_initialisation_step(
442 result, species_sets, verbose
443 )
444 return result
445
446
447def initialisation_steps_onto_task_graph_multiscale_sort_coalesced_memory(
448 species_sets,
449 verbose,
450):
451 """!
452
453 @see map_onto_separate_mesh_traversals()
454
455 @param concatenate_steps_of_species: [Species]
456 Sequence of particle species to be handled.
457
458 @param verbose: {True, False}
459
460 """
462 sequence_of_steps=concatenate_steps_of_species(species_sets, True),
463 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_WITH_LIFTS_AND_DROPS_COALESCED_PARTICLE_STORAGE,
464 step_type=StepType.INITIALISATION,
465 verbose=verbose,
466 )
467 add_clear_task_number_to_very_first_initialisation_step(
468 result, species_sets, verbose
469 )
470 return result
471
472
473def initialisation_steps_onto_task_graph_bucket_sort_coalesced_memory(
474 species_sets,
475 verbose,
476):
477 """!
478
479 @see map_onto_separate_mesh_traversals()
480
481 @param concatenate_steps_of_species: [Species]
482 Sequence of particle species to be handled.
483
484 @param verbose: {True, False}
485
486 """
488 sequence_of_steps=concatenate_steps_of_species(species_sets, True),
489 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_ON_THE_FLY_COALESCED_PARTICLE_STORAGE,
490 step_type=StepType.INITIALISATION,
491 verbose=verbose,
492 )
493 add_clear_task_number_to_very_first_initialisation_step(
494 result, species_sets, verbose
495 )
496 return result
497
498
499def particle_steps_onto_task_graph_multiscale_sort_scattered_memory(
500 species_sets,
501 verbose,
502):
503 """!
504
505 For a sequential ordering, i.e. 1:1 mapping of steps per species onto
506 traversals, it makes no real difference how we order the progress through
507 the individual steps. So I just concatenate the steps per species.
508
509 @see map_onto_separate_mesh_traversals()
510
511 @param concatenate_steps_of_species: [Species]
512 Sequence of particle species to be handled.
513
514 @param verbose: {True, False}
515
516 """
518 sequence_of_steps=concatenate_steps_of_species(species_sets, False),
519 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_WITH_LIFTS_AND_DROPS_SCATTERED_PARTICLE_STORAGE,
520 step_type=StepType.TIME_STEP,
521 verbose=verbose,
522 )
523
524
525def particle_steps_onto_task_graph_bucket_sort_scattered_memory(
526 species_sets,
527 verbose,
528):
529 """!
530
531 For a sequential ordering, i.e. 1:1 mapping of steps per species onto
532 traversals, it makes no real difference how we order the progress through
533 the individual steps. So I just concatenate the steps per species.
534
535 @see map_onto_separate_mesh_traversals()
536
537 @param concatenate_steps_of_species: [Species]
538 Sequence of particle species to be handled.
539
540 @param verbose: {True, False}
541
542 """
544 sequence_of_steps=concatenate_steps_of_species(species_sets, False),
545 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_ON_THE_FLY_SCATTERED_PARTICLE_STORAGE,
546 step_type=StepType.TIME_STEP,
547 verbose=verbose,
548 )
549
550
551def particle_steps_onto_task_graph_multiscale_sort_coalesced_memory(
552 species_sets,
553 verbose,
554):
555 """!
556
557 For a sequential ordering, i.e. 1:1 mapping of steps per species onto
558 traversals, it makes no real difference how we order the progress through
559 the individual steps. So I just concatenate the steps per species.
560
561 @see map_onto_separate_mesh_traversals()
562
563 @param concatenate_steps_of_species: [Species]
564 Sequence of particle species to be handled.
565
566 @param verbose: {True, False}
567
568 """
570 sequence_of_steps=concatenate_steps_of_species(species_sets, False),
571 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_WITH_LIFTS_AND_DROPS_COALESCED_PARTICLE_STORAGE,
572 step_type=StepType.TIME_STEP,
573 verbose=verbose,
574 )
575
576
577def particle_steps_onto_task_graph_bucket_sort_coalesced_memory(
578 species_sets,
579 verbose,
580):
581 """!
582
583 For a sequential ordering, i.e. 1:1 mapping of steps per species onto
584 traversals, it makes no real difference how we order the progress through
585 the individual steps. So I just concatenate the steps per species.
586
587 @see map_onto_separate_mesh_traversals()
588
589 @param concatenate_steps_of_species: [Species]
590 Sequence of particle species to be handled.
591
592 @param verbose: {True, False}
593
594 """
596 sequence_of_steps=concatenate_steps_of_species(species_sets, False),
597 particle_sorting_and_storage=ParticleSortingAndStorage.SORT_ON_THE_FLY_COALESCED_PARTICLE_STORAGE,
598 step_type=StepType.TIME_STEP,
599 verbose=verbose,
600 )
map_onto_separate_mesh_traversals_constructing_task_graph(sequence_of_steps, ParticleSortingAndStorage particle_sorting_and_storage, StepType step_type, verbose)
Simpest graph compiler constructing a task tree.
Definition TaskGraph.py:306
translate_one_step_of_species_into_mesh_traversal(step, current_species_set, mesh_traversals, alter_particle_position, use_continuous_particles_sequences, StepType step_type, verbose, synchronise_task_graph_after_each_mesh_sweep=True)
Translate one step of species into mesh traversal.
Definition TaskGraph.py:37