Peano
Loading...
Searching...
No Matches
AMRLogic.py
Go to the documentation of this file.
1# This file is part of the Peano project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import jinja2
4import peano4.api
5
6
7from peano4.api.solversteps import Steps
9 DynamicMeshRefinementAnalysis,
10)
11from exaparty.api.actionsets.ScatterGlobalMemory import ScatterGlobalMemory
12from exaparty.api.actionsets.DynamicMeshRefinementTrigger import DynamicMeshRefinementTrigger
13
14
15def add_dynamic_mesh_refinement_and_particle_resorting(
16 steps, alter_position, species_sets, sort_with_lifts_and_drops, verbose
17):
18 """!
19
20 Add action sets to step (aka mesh traversal) to adopt mesh to particle configuration
21
22
23 The AMR logic is based upon the observation that Peano's toolbox always
24 holds the particles in the right place and hence allows us to realise
25 all particle-particle interactions. If we don't get the mesh right, we
26 might not be optimal in terms of cost. But we are never wrong!
27
28 ## Straightforward workflow
29
30 In principle, SPH's AMR logic is pretty simple in the context of a the dual
31 tree with its lifts and drops. The illustration below shows this trivial
32 workflow on the left side:
33
34 @image html AMR-logic.png
35
36 The image is the time axis and the bullets are the individual mesh
37 traversals. On the left, we see the user input which eventually triggers a
38 cascade of actions on the bookkeeping side (right). The user alters the
39 particle position and hence might invalidate the particle-grid association.
40 We have to re-sort. Some sorting can happen right in the same mesh traversal
41 in which we alter the particles' properties in the touchCellLastTime or
42 touchVertexLastTime actions. The green colour denotes stuff that's happening
43 in the last data access - which might also introduce some MPI sends.
44
45 In the subsequent mesh traversal, we have to receive MPI data, move particles
46 down in their tree and associate that to the correct vertex. This happens
47 prior to any operation (denoted blue). Now that particles are in the right
48 place, we can update their parallel state.
49
50 A straightforward implementation now might evaluate the AMR criterion, as
51 all particles are in place and we can do so. Once we have collected all AMR
52 info, we can distribute it globally and hand it out in the subsequent mesh
53 traversal in which we consequently trigger refinements. Peano does not
54 realise refinements immediately, but waits for the next mesh traversal to
55 do so.
56
57 When we have done the refinement, we have to resort. Again, the resort has
58 to spread over two mesh traversals, as the first half of the sorting might
59 trigger MPI exchanges and multilevel traversals.
60
61 In this native data flow scheme, the user action affects the behaviour of
62 five follow-up mesh traversals.
63
64
65 Alternative to add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption()
66 which is compatible with memory pool solutions, as long as you insert a dummy mesh sweep
67 after each routine which changes the particle-mesh topology (or you ensure that the
68 subsequent sweep does not evaluate particle-particle interactions).
69
70 The big difference to add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption()
71 is that we do analyse the AMR after we've resorted the particles. However, we
72 do not realise the AMR up until we have to due to physics. So we make the AMR
73 lag behind even further.
74
75 The big differences hence arise in the section where we do the association, and
76 then we introduce the additional gather routines. As we do not update the
77 association four times but only twice, we can also update the state only once
78 instead of two times.
79
80 ## Workflow optimised towards minimal resorting
81
82 The optimised workflow accepts that we we are fine to deal with a suboptimal
83 mesh layout, as long as we minimise the number of particle movements. This
84 is a different cost objective than in the description above. We realise that
85 we will always need a resort when we have updated a mesh.
86
87 The idea is that we know that a user interaction forces us to update the
88 particle-mesh association, while AMR does not: We have to lift particles
89 from areas of the tree which are coarsened, but otherwise AMR does not
90 invalidate a valid mesh association. Particles might reside on too coarse
91 levels, but still all the kernel evaluations remain correct. The lift is the
92 only thing that causes issues, as it might mean that particles literally
93 travel.
94
95 To avoid that we have to do two sorts per particle position update, we
96 evaluate the AMR criterion two grid sweeps ahead of the actual particle
97 position update. This way, the AMR realisation and the actual position
98 update coincide in the same mesh sweep. No need for an additional sorting
99 step.
100
101 The disadvantage of this scheme is that the mesh always lags behind. The
102 AMR just is about the realise when the particles move again. So it might
103 have been optimal before, but now it is not good anymore. In return, we
104 however get a scheme which does, amortised, need only one resorting step
105 per mesh update plus AMR update.
106
107
108 ## Interplay with gathering
109
110 The aggressive AMR is not compatible with particles that gather data in memory
111 pools.
112
113 ## Realisation
114
115 This implementation puts emphasis that the mesh is always as tighly
116 adopted to the particles as possible. If necessary, this might mean
117 that we resort the particles multiple times per grid sweep: If they
118 change their position, we resort. But once we have resorted, we might
119 want to change the mesh, which in turn means we have to resort again,
120 as we might have new cells or cells might have been deleted.
121
122
123 ## Order of action sets
124
125 As so often, the order of the action sets is tremendously important,
126 as the individual action sets interact with each other. When we
127 discuss the order here, we may assume that all the function steps
128 aka action sets are already fully implemented and in the right order.
129
130 The trigger the AMR can be integrated whereever we want within the
131 pipeline. It simply deploys events, i.e. it is order-agnostic.
132
133 For the remaining action sets, we observe the following properties:
134
135 - Mesh analysis: Looks into the cell stats. These are in place, so
136 it should be one of the last things. The analysis happens in
137 touchCellLastTime().
138 - UpdateParallelState sets the particle flags in touchCellFirstTime().
139 In the last access to the vertex, it looks at all particles' flags
140 and throws away particles which are virtual. It is iportant that
141 that flag update happens when all particles are properly in place.
142 The throwing away should be the very last thing, though we can
143 throw away particles before we move them around if we want.
144 - UpdateAssociation. Should be the very first thing, but should
145 happen, before we update the state. In principle, the state can
146 also come later, but we might mess up the stats if it comes
147 before the particle migration: it might count particles multiple
148 times.
149
150 So we want to have the following lifecycle from a grid's point of
151 view:
152
153 - touchVertexFirstTime():
154 - Sort in the particles from coarser meshes or the global sieve list.
155 - Set their new parallel state.
156 - Then the user-defined stuff.
157 - touchCellFirstTime():
158 - Mark local particles as local, but it doesn't really matter when
159 this happens.
160 - touchCellLastTime():
161 - User code.
162 - Maintain some mesh statistics.
163 - touchVertexLastTime():
164 - User-defined stuff.
165 - Resort particles (to coarser levels, e.g.).
166 - Throw away virtual (remote) particles.
167
168
169 ## Arguments
170
171 @param steps: [peano4.api.solversteps.Step]
172
173 @param alter_position: [{True,False}]
174
175 @param species_sets: [Species]
176
177 @param verbose: {True, False}
178 Controls how much debug information is dropped.
179
180 """
181 if len(steps) != len(alter_position):
182 raise ValueError(
183 "number of steps does not equal number of alter_position flags: {} vs {}".format(
184 steps, alter_position
185 )
186 )
187
188 for current_species_set in species_sets:
189 global_steps_to_evaluate_amr = [False for i in range(0, len(steps))]
190 global_steps_to_update_particle_grid_assocation = [
191 False for i in range(0, len(steps))
192 ]
193 global_steps_to_update_parallel_state = [False for i in range(0, len(steps))]
194 global_steps_to_realise_amr = [False for i in range(0, len(steps))]
195
196 for i in range(0, len(steps)):
197 if alter_position[i]:
198 if verbose:
199 print(
200 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: step {} alters the particle positions of species {}".format(
201 i, current_species_set.name
202 )
203 )
204
205 #
206 # AMR
207 # =======
208 #
209 steps_to_evaluate_amr = {(i + 1) % len(steps)}
210 if verbose:
211 print(
212 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: steps {} have to evaluate the AMR criterion".format(
213 steps_to_evaluate_amr
214 )
215 )
216 for j in steps_to_evaluate_amr:
217 global_steps_to_evaluate_amr[j] = True
218
219 #
220 # Association
221 # ==============
222 #
223 steps_to_update_particle_grid_assocation = {
224 (i + 0) % len(steps),
225 (i + 1) % len(steps),
226 }
227 if verbose:
228 print(
229 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: steps {} have to update the particle-grid association of species {}".format(
230 steps_to_update_particle_grid_assocation,
231 current_species_set.name,
232 )
233 )
234 for j in steps_to_update_particle_grid_assocation:
235 global_steps_to_update_particle_grid_assocation[j] = True
236
237 #
238 # State
239 # =========
240 #
241 # Action set peano4.api.toolbox.UpdateParallelState also eliminates
242 # halo values and therefore has to be invoked within each drift,
243 # too
244 #
245 steps_to_update_parallel_state = {
246 (i + 0) % len(steps),
247 (i + 1) % len(steps),
248 }
249 if verbose:
250 print(
251 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: steps {} have to update the parallel state of species {}".format(
252 steps_to_update_parallel_state, current_species_set.name
253 )
254 )
255 for j in steps_to_update_parallel_state:
256 global_steps_to_update_parallel_state[j] = True
257
258 #
259 # Realise AMR
260 # ===============
261 #
262 # steps_to_realise_amr = {(i + 2) % len(steps)}
263 steps_to_realise_amr = {(i - 1) % len(steps)}
264 if verbose:
265 print(
266 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: steps {} have to realise the AMR instructions".format(
267 steps_to_realise_amr
268 )
269 )
270 for j in steps_to_realise_amr:
271 global_steps_to_realise_amr[j] = True
272
273 for i in range(0, len(steps)):
274 if global_steps_to_evaluate_amr[i]:
275 action_set_AMR_analysis = DynamicMeshRefinementAnalysis(
276 current_species_set,
277 min_particles_per_cell=int(
278 current_species_set.particle_model.particles_per_cell / 27
279 ),
280 max_particles_per_cell=current_species_set.particle_model.particles_per_cell,
281 min_h=current_species_set.particle_model.min_h,
282 max_h=current_species_set.particle_model.max_h,
283 )
284 action_set_AMR_analysis.descend_invocation_order = (
285 steps[i].lowest_descend_invocation_order() - 1
286 )
287 action_set_AMR_analysis.parallel = True
288 steps[i].add_action_set(action_set_AMR_analysis)
289 if verbose:
290 print(
291 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: add peano4.api.toolbox.particles.DynamicMeshRefinementAnalysis for species {} to step {}".format(
292 current_species_set.name, i
293 )
294 )
295
296 if global_steps_to_update_parallel_state[i]:
297 action_set_update_parallel_state = (
299 particle_set=current_species_set,
300 )
301 )
302 action_set_update_parallel_state.descend_invocation_order = (
303 steps[i].lowest_descend_invocation_order() - 1
304 )
305 action_set_update_parallel_state.parallel = True
306 steps[i].add_action_set(action_set_update_parallel_state)
307 if verbose:
308 print(
309 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: add peano4.api.toolbox.particles.UpdateParallelState for species {} to step {}".format(
310 current_species_set.name, i
311 )
312 )
313 # assert (
314 # not current_species_set.generator.gather_particles
315 # ), "the aggressive AMR is not compatible with particles that gather data in memory pools"
316 # @todo Scattering is not in yet
317
318 if global_steps_to_realise_amr[i]:
319 action_set_dynamic_AMR = DynamicMeshRefinementTrigger()
320 action_set_dynamic_AMR.descend_invocation_order = (
321 steps[i].highest_descend_invocation_order() + 1
322 )
323 action_set_dynamic_AMR.parallel = True
324 steps[i].add_action_set(action_set_dynamic_AMR)
325 if verbose:
326 print(
327 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: add exaparty.api.actionsets.DynamicMeshRefinementTrigger for species {} to step {}".format(
328 current_species_set.name, i
329 )
330 )
331
332 if global_steps_to_update_particle_grid_assocation[i]:
333 if sort_with_lifts_and_drops:
334 action_set_update_particle_grid_association = peano4.api.toolbox.particles.api.UpdateParticleGridAssociation_LiftDrop(
335 current_species_set
336 )
337 else:
338 action_set_update_particle_grid_association = peano4.api.toolbox.particles.api.UpdateParticleGridAssociation_BucketSort(
339 current_species_set
340 )
341 action_set_update_particle_grid_association.descend_invocation_order = (
342 steps[i].lowest_descend_invocation_order() - 1
343 )
344 action_set_update_particle_grid_association.parallel = True
345 steps[i].add_action_set(action_set_update_particle_grid_association)
346 if verbose:
347 print(
348 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_aggressive_mesh_adoption]: add peano4.api.toolbox.particles.api.UpdateParticleGridAssociation specialisation for species {} to step {}".format(
349 current_species_set.name, i
350 )
351 )
352
353 if current_species_set.generator.gather_particles:
354 action_set_gather = (
356 particle_set=current_species_set,
357 )
358 )
359 action_set_gather.descend_invocation_order = (
360 steps[i].lowest_descend_invocation_order() - 1
361 )
362 action_set_gather.parallel = True
363
364 action_set_scatter = ScatterGlobalMemory(
365 particle_set=current_species_set,
366 )
367 action_set_scatter.descend_invocation_order = (
368 steps[i].lowest_descend_invocation_order() - 1
369 )
370 action_set_scatter.parallel = True
371
372 #
373 # We have to add it once more, as a sieve or drop might trigger a "new"
374 # neighbour exchange which then has to be taken into account on the
375 # receiving side
376 #
377 steps[i].add_action_set(action_set_gather)
378 steps[i].add_action_set(action_set_scatter)
379 steps[(i + 1) % (len(steps))].add_action_set(action_set_gather)
380 if verbose:
381 print(
382 "GRAPH COMPILER DEBUG [add_dynamic_mesh_refinement_and_particle_resorting_minimise_resorting_and_add_gathers]: add peano4.api.toolbox.particles.GatherParticlesInMemoryPool for species {} to step {} and {}".format(
383 current_species_set.name, i, (i + 1) % (len(steps))
384 )
385 )
Update the parallel state of particles and keep stats of them.
The Python-side model of "what happens during one mesh sweep".
Definition __init__.py:1