Peano
Loading...
Searching...
No Matches
Particle.py
Go to the documentation of this file.
1# use, please see the copyright notice at www.peano-framework.org
3
4import dastgen2
5from swift2.particle.AlgorithmStep import AlgorithmStep
6from abc import abstractmethod
7import copy
8
9
11 """!
12
13 Base class for any particle in the project
14
15 You usually do not initialise this base class, but you pick one of the
16 subclasses which define which time stepping scheme is realised by a
17 particle. The time stepping scheme defines through which phases or steps
18 each particle runs through per time step, while this generic base class
19 has no steps at all.
20
21 The type is a specialisation of Peano's particle as it is provided via the
22 toolbox. In line with the docu there, you can use this particle and add
23 further attributes via
24
25 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 my_particle.data.add_attribute( peano4.dastgen2.Peano4DoubleArray("v","Dimensions") )
27 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28
29
30
31 """
32
33 class SpeciesAspect(object):
34 """
35
36 Every generated particle belongs to a species. I originally thought I'd
37 use some kind of inheritance to model this, but actually it is easier to
38 introduce a static field for the time being that stores the species
39 information.
40
41 """
42
43 def __init__(self):
44 pass
45
46 def get_include(self):
47 return """
48#include "swift2/ParticleSpecies.h"
49"""
50
51 def set_model(self, data_model):
52 self._data_model = data_model
53
54 def get_attributes(self):
55 return ""
56
57 def get_method_declarations(self, full_qualified_name):
58 return """
59 static ::swift2::ParticleSpecies& getSpecies();
60"""
61
62 def get_implementation(self, full_qualified_name):
63 return (
64 """
65::swift2::ParticleSpecies& """
66 + full_qualified_name
67 + """::getSpecies() {
68 static ::swift2::ParticleSpecies species;
69 return species;
70}
71"""
72 )
73
75 self,
76 name,
77 particles_per_cell,
78 min_h,
79 max_h,
80 ):
81 """!
82
83 Initialise the particle
84
85 This is the baseclass for a particle, i.e. we only add the absolute minimum
86 of information to a particle. As we inherit from the toolbox particle, we
87 already have some attributes defined. These are the guys which are not
88 explicitly visible from the code snippet below, i.e. they are introduced by
89 the superclass constructor.
90
91 Here's an overview of pre-defined attributes that each and every particle
92 hosts:
93
94 - A position which is a double vector with Dimension entries.
95 - A search radius which describes the range with which other particles a
96 particle might theoretically interact. The effective interaction might
97 be smaller if a code decides to ignore potential interaction partners,
98 i.e. this is an absolute maximum. The search radius will decide on which
99 resolution level within the tree a particle is held.
100 - A MoveState flag. This flag is essential. We use it later to label those
101 particles that have been moved to avoid that we move particles multiple
102 times.
103 - A flag that indicates if a particle has been updated within a cell.
104 Consult our code release papers: In short, a particle does not uniquely
105 belong to one cell but can belong to many cells. We nevertheless want to
106 update them only once.
107
108 @see swift2.actionsets.UpdateParticleMarker
109
110 """
111 super(Particle, self).__init__(name)
112
113 self.data.add_attribute(
115 "MoveState", ["New", "NotMovedYet", "Moved"]
116 )
117 )
118 self.data.add_attribute(dastgen2.attributes.Boolean("CellHasUpdatedParticle"))
119 self.data.add_aspect(Particle.SpeciesAspect())
120
121 if particles_per_cell <= 0:
122 print(
123 "WARNING: Particles per cell set to zero, so code might refine up to maximum depth even if there is only one particle"
124 )
125 self.particles_per_cell = particles_per_cell
126 self.min_h = min_h
127 self.max_h = max_h
128
129 return
130
131 @abstractmethod
133 """!
134
135 Return sequence of algorithm steps that have to be called per time step
136
137 """
138 assert False, "Should be overwritten by subclass"
139 return []
140
141 @abstractmethod
143 """!
144
145 Return sequence of algorithm steps that have to be performed throughout initialisation
146
147 """
148 assert False, "Should be overwritten by subclass"
149 return []
150
151 @property
153 """!
154
155 Create default readme descriptor
156
157 You might want to overwrite this for your particular particle
158 species to get more detailed info the the README.md file generated
159 by Peano.
160
161 """
162 return (
163 """### Particle """
164 + self.namenamename
165 + """
166
167Particle properties:
168
169- particles per cell: """
170 + str(self.particles_per_cell)
171 + """
172- h_min: """
173 + str(self.min_h)
174 + """
175- h_max: """
176 + str(self.max_h)
177 + """
178- attributes: """
179 + str(len(self.data._attributes))
180 + """
181
182 """
183 )
184
185 DependencyChecks_Attribute_Prefix = "dependencyChecks"
186 DependencyChecks_Ifdefs = ["PeanoDebug > 0"]
187
189 self,
190 steplist,
191 step_type_name,
192 peano4_event_enum,
193 ):
194 """!
195
196 Add dependency checks as well as mesh consistency checks to the algorithm steps
197
198 This function actually modifies the AlgorithmSteps to contain the
199 debug dependency checks. Is called by self._add_dependency_checks()
200 and uses the validation routines from swift2::dependencychecks.
201
202 The routine is used to either add checks for the init phase or the
203 actual time stepping. Which one is meant is identified through
204 step_type_name. You can obviously call the routine twice: once for
205 init and once for the time stepping. Later on, you might want to
206 add further steps.
207
208 Besides the name of interest, we also require a list of algorithmic
209 steps through which the code runs through.
210
211
212 ## Default settings
213
214 If the user does not define bespoke default settings, this routine adds
215 default ones. These are set through instances of swift::dependencychecks::Invariant.
216
217 - For vertices, this is touch-at-most-once-mask-out-otherwise-all-previous-steps-update-at-least-once.
218 - For cells, this is also touch-at-most-once-mask-out-otherwise-all-previous-steps-update-at-least-once.
219
220 ## Additional attributes
221
222 First, the particle is added two counters which count up how often a
223 particle is touched or masked out.
224
225 @param Steplist: list
226 list of AlgorithmStep objects to work on
227
228 @param step_type_name: str
229 name of type of steps we're dealing with. Either AlgorithmStep
230 or InitStep.
231
232 @param peano4_event_enum: list
233 list of enums of peano4 events within a single mesh traversal
234 (e.g. touch vertex first time, cell kernel, ...)
235
236 @return steps: list
237 the modified list of AlgorithmSteps, now including dependency checks
238
239 """
240 steps = copy.deepcopy(steplist)
241 nsteps = len(steps)
242 nevents = AlgorithmStep.PeanoEventUsedBySwift.EVENT_COUNT.value
243
244 # add enum to particle for each AlgorithmStep. This enum tracks in
245 # which algorithmic step a particle currently is. See
246 # DependencyChecks.h.
247 step_name_list = [step.name for step in steps]
248 step_name_list.append("count") # add a count so we can loop over enums
249 step_name_enum = dastgen2.attributes.Enumeration(
250 self.DependencyChecks_Attribute_Prefix + step_type_name + "LastUpdated",
251 step_name_list,
252 ifdefs=self.DependencyChecks_Ifdefs,
253 )
254 self.data.add_attribute(step_name_enum)
255
256 # add (flattened) array for flags
257 number_of_updates = dastgen2.attributes.IntegerArray(
258 self.DependencyChecks_Attribute_Prefix + step_type_name + "Updates",
259 nsteps * nevents,
260 ifdefs=self.DependencyChecks_Ifdefs,
261 )
262 self.data.add_attribute(number_of_updates)
263
264 number_of_mask_outs = dastgen2.attributes.IntegerArray(
265 self.DependencyChecks_Attribute_Prefix + step_type_name + "MaskOuts",
266 nsteps * nevents,
267 ifdefs=self.DependencyChecks_Ifdefs,
268 )
269 self.data.add_attribute(number_of_mask_outs)
270
271 # String template for dependency check call
272 check_template_cell_operation = """
273 ::swift2::dependencychecks::check{STEP_TYPE}(
274 ::swift2::dependencychecks::Invariant::{INVARIANT},
275 localParticles,
276 activeParticles,
277 marker,
278 globaldata::{NAME}::DependencyChecks{STEP_TYPE}LastUpdated::{STEP_NAME},
279 globaldata::{NAME}::{PEANO4_EVENT_TYPE}::{EVENT},
280 {CHECK_FUNCTION},
281 _spacetreeId
282 );
283"""
284
285 check_template_vertex_operation = """
286 ::swift2::dependencychecks::check{STEP_TYPE}(
287 ::swift2::dependencychecks::Invariant::{INVARIANT},
288 assignedParticles,
289 marker,
290 globaldata::{NAME}::DependencyChecks{STEP_TYPE}LastUpdated::{STEP_NAME},
291 globaldata::{NAME}::{PEANO4_EVENT_TYPE}::{EVENT},
292 {CHECK_FUNCTION},
293 _spacetreeId
294 );
295"""
296
297 mark_template_cell_operation = """
298 ::swift2::dependencychecks::mark{STEP_TYPE}(
299 localParticles,
300 activeParticles,
301 marker,
302 globaldata::{NAME}::DependencyChecks{STEP_TYPE}LastUpdated::{STEP_NAME},
303 globaldata::{NAME}::{PEANO4_EVENT_TYPE}::{EVENT},
304 ::swift2::dependencychecks::Invariant::{INVARIANT},
305 {CHECK_FUNCTION},
306 _spacetreeId
307 );
308"""
309
310 mark_template_vertex_operation = """
311 ::swift2::dependencychecks::mark{STEP_TYPE}(
312 assignedParticles,
313 marker,
314 globaldata::{NAME}::DependencyChecks{STEP_TYPE}LastUpdated::{STEP_NAME},
315 globaldata::{NAME}::{PEANO4_EVENT_TYPE}::{EVENT},
316 ::swift2::dependencychecks::Invariant::{INVARIANT},
317 {CHECK_FUNCTION},
318 _spacetreeId
319 );
320"""
321
322 check_template_vertex_operation_local_particles_after_sweep = """
323 ::swift2::dependencychecks::checkParticlesAssignedToVertexInTouchLastTime{STEP_TYPE}(
324 ::swift2::dependencychecks::Invariant::{INVARIANT},
325 assignedParticles,
326 globaldata::{NAME}::DependencyChecks{STEP_TYPE}LastUpdated::{STEP_NAME},
327 _spacetreeId
328 );
329"""
330
331 for i, step in enumerate(steps):
332 d = {
333 "NAME": self.namenamename,
334 "STEP_TYPE": step_type_name,
335 "STEP_NAME": step.name,
336 "PEANO4_EVENT_TYPE": peano4_event_enum.get_accessor_name(),
337 "CHECK_FUNCTION": None,
338 "EVENT": None,
339 "INVARIANT": None,
340 }
341
342 step.prepare_traversal_kernel = (
343 """
344 toolbox::particles::assignmentchecks::startMeshSweep( "{}" );
345 """.format(
346 step.name
347 )
348 + step.prepare_traversal_kernel
349 )
350
351 if (
352 step.touch_vertex_first_time_dependency_policy == None
353 and AlgorithmStep.may_trigger_rerun(step.effect)
354 ):
355 d[
356 "INVARIANT"
357 ] = "TouchFirstUsage_MaskOutAfterwards_AllPreviousStepsUpdateAtLeastOnce_SweepMayRerun"
358 elif step.touch_vertex_first_time_dependency_policy == None:
359 d[
360 "INVARIANT"
361 ] = "TouchFirstUsage_MaskOutAfterwards_AllPreviousStepsUpdateAtLeastOnce"
362 else:
363 d["INVARIANT"] = step.touch_vertex_first_time_dependency_policy
364
365 # TOUCH VERTEX FIRST TIME
366 # -----------------------
367 d["EVENT"] = AlgorithmStep.get_event_name(
368 AlgorithmStep.PeanoEventUsedBySwift.TOUCH_VERTEX_FIRST_TIME
369 )
370 d[
371 "CHECK_FUNCTION"
372 ] = "::swift2::kernels::localParticleCanBeUpdatedInVertexKernel<globaldata::{}>".format(
373 self.namenamename
374 )
375
376 if step.touch_vertex_first_time_kernel is None:
377 step.touch_vertex_first_time_kernel = ""
378
379 if i == 0:
380 # Only exception is first AlgorithmStep: There, first re-set
381 # all counters
382 step.touch_vertex_first_time_kernel = (
383 "\n ::swift2::dependencychecks::clearDependencyChecks"
384 + step_type_name
385 + "(assignedParticles);\n"
386 + mark_template_vertex_operation.format(**d)
387 + step.touch_vertex_first_time_kernel
388 )
389 else:
390 step.touch_vertex_first_time_kernel = (
391 mark_template_vertex_operation.format(**d)
392 + step.touch_vertex_first_time_kernel
393 )
394
395 # Finally, add the dependency check *after* the actual function call
396 step.touch_vertex_first_time_kernel += (
397 check_template_vertex_operation.format(**d)
398 )
399
400 # CELL_KERNEL
401 # -----------
402 d["EVENT"] = AlgorithmStep.get_event_name(
403 AlgorithmStep.PeanoEventUsedBySwift.CELL_KERNEL
404 )
405 d[
406 "CHECK_FUNCTION"
407 ] = "::swift2::kernels::localParticleCanBeUpdatedInCellKernelFromAnyOtherParticle<globaldata::{}>".format(
408 self.namenamename
409 )
410
411 if (
412 step.cell_kernel_dependency_policy == None
413 and AlgorithmStep.may_trigger_rerun(step.effect)
414 ):
415 d[
416 "INVARIANT"
417 ] = "TouchAtMostOnce_MaskOutOtherwise_AllPreviousStepsUpdateAtLeastOnce_SweepMayRerun"
418 elif step.cell_kernel_dependency_policy == None:
419 d[
420 "INVARIANT"
421 ] = "TouchAtMostOnce_MaskOutOtherwise_AllPreviousStepsUpdateAtLeastOnce"
422 else:
423 d["INVARIANT"] = step.cell_kernel_dependency_policy
424
425 if step.cell_kernel is None:
426 step.cell_kernel = ""
427
428 step.cell_kernel = (
429 mark_template_cell_operation.format(**d) + step.cell_kernel
430 )
431
432 # Now add the dependency check *after* the actual function call
433 step.cell_kernel += check_template_cell_operation.format(**d)
434
435 # TOUCH VERTEX LAST TIME
436 # ----------------------
437 d["EVENT"] = AlgorithmStep.get_event_name(
438 AlgorithmStep.PeanoEventUsedBySwift.TOUCH_VERTEX_LAST_TIME
439 )
440 d[
441 "CHECK_FUNCTION"
442 ] = "::swift2::kernels::localParticleCanBeUpdatedInVertexKernel<globaldata::{}>".format(
443 self.namenamename
444 )
445
446 if (
447 step.touch_vertex_last_time_dependency_policy == None
448 and AlgorithmStep.may_trigger_rerun(step.effect)
449 ):
450 d[
451 "INVARIANT"
452 ] = "TouchFirstUsage_MaskOutAfterwards_AllPreviousStepsUpdateAtLeastOnce_SweepMayRerun"
453 elif step.touch_vertex_last_time_dependency_policy == None:
454 d[
455 "INVARIANT"
456 ] = "TouchFirstUsage_MaskOutAfterwards_AllPreviousStepsUpdateAtLeastOnce"
457 else:
458 d["INVARIANT"] = step.touch_vertex_last_time_dependency_policy
459
460 if step.touch_vertex_last_time_kernel is None:
461 step.touch_vertex_last_time_kernel = ""
462
463 step.touch_vertex_last_time_kernel = (
464 mark_template_vertex_operation.format(**d)
465 + step.touch_vertex_last_time_kernel
466 )
467
468 # Now add the dependency check *after* the actual function call
469 step.touch_vertex_last_time_kernel += (
470 check_template_vertex_operation.format(**d)
471 )
472
473 # touch vertex last time is special: Add additional dependency check
474 # which makes sure all activeParticles have been local at some point
475 # during this algorithm step
476 step.touch_vertex_last_time_kernel += (
477 check_template_vertex_operation_local_particles_after_sweep.format(**d)
478 )
479
480 return steps
481
483 """!
484
485 Add dependency (particle consistency) checks
486
487 This routine should be called in the subclass directly after you have
488 befilled self._algorithm_steps and self._initialisation_steps. It is
489 safe to add this stuff to your particle model all the time - the
490 actual checks kick in if and only if you run in non-release modes.
491 However, you might want to call the function only in these cases to
492 ensure that no (empty) C++ code is generated and the compiler can
493 optimise more aggressively.
494
495 ## Realisation
496
497 We first create Enumerations representing each
498 algorithm step defined for the Particle, as well as each sweep stage of
499 any grid traversal.
500
501 Then for each defined algorithm step, we add a call to the dependency
502 check functions for each sweep stage (touch vertex first time, cell
503 kernel, touch vertex last time...)
504
505 The check consists of
506 1) marking down which step in the full algorithm a particle has
507 completed in the current simulation step
508 2) verifying that each step and sweep stage before the one the particle
509 currently finds itself in has been completed.
510
511 This is done both for the main algorithm steps as well as for the
512 initialisation steps individually.
513
514
515 """
516
517 ifdefs = ["PeanoDebug > 0"]
518
519 # First add enum to particle class for each sweep stage. These are identical
520 # for both algorithm steps and initialisation steps.
521 peano4_event_enum = dastgen2.attributes.Enumeration(
522 self.DependencyChecks_Attribute_Prefix + "PeanoEventUsedBySwift",
523 [
524 AlgorithmStep.get_event_name(s)
526 ],
527 ifdefs=ifdefs,
528 )
529 self.data.add_attribute(peano4_event_enum)
530
531 # Add debug dependency checks to the actual algorithm steps:
532 steplist = self.algorithm_steps()
533 step_type_name = "AlgorithmStep"
534 modified_steps = self._dependency_checks_modify_steps(
535 steplist, step_type_name, peano4_event_enum
536 )
537 self._algorithm_steps = modified_steps
538
539 # Same but for initialisation steps
540 steplist = self.initialisation_steps()
541 step_type_name = "InitStep"
542 modified_steps = self._dependency_checks_modify_steps(
543 steplist, step_type_name, peano4_event_enum
544 )
545 self._initialisation_steps = modified_steps
Wrapper around C++ enumerations which is not a datatype supported natively by MPI.
Definition Enumeration.py:6
Enumeration of different Peano events during a grid traversal into which Swift 2 plugs in.
Every generated particle belongs to a species.
Definition Particle.py:33
get_method_declarations(self, full_qualified_name)
Definition Particle.py:57
get_implementation(self, full_qualified_name)
Definition Particle.py:62
Base class for any particle in the project.
Definition Particle.py:10
algorithm_steps(self)
Return sequence of algorithm steps that have to be called per time step.
Definition Particle.py:132
_add_dependency_checks(self)
Add dependency (particle consistency) checks.
Definition Particle.py:482
__init__(self, name, particles_per_cell, min_h, max_h)
Initialise the particle.
Definition Particle.py:80
_dependency_checks_modify_steps(self, steplist, step_type_name, peano4_event_enum)
Add dependency checks as well as mesh consistency checks to the algorithm steps.
Definition Particle.py:193
initialisation_steps(self)
Return sequence of algorithm steps that have to be performed throughout initialisation.
Definition Particle.py:142