9from .AbstractUpdateParticleGridAssociation
import AbstractUpdateParticleGridAssociation
15 Associate particles to spacetree vertices
17 Sort particles into the respective levels and cells. This mapping should
18 be used every time you move particles around.
23 The present algorithm is a bucket sort. It consists of very few, simple
26 - If a particle has not moved or has moved but not approached another
27 vertex, leave it where it is. We sort incrementally.
28 - If a particle has moved and is not associated to its closest vertex
29 anymore, add it to a global list.
30 - If a particle has increased its cut-off radius and this cut-off
31 radius does not fit into the present mesh resolution anymore, add it
33 - Exchange the global list between all ranks after each sweep.
34 - If a particle from the global list fits into a cell, add it to the
38 ## Dynamic adaptive mesh refinement
40 If the grid is refined around a certain vertex, we usually have to drop
41 particles associated to the coarser vertex - which is now a refined one -
42 to the next finer level. However, such a drop would violate our bucketing
43 principle. The other sorting algorithms are fine with drops, but we are
46 What we do instead is that we identify such particles and lift them into
47 the sieve set. Which is counterintuitive as we lift particles which should
48 be dropped. However, we know that the subsequent mesh sweep then will
49 insert them into the right level. That is, we map a drop operation onto
50 a lift-globally-sieve sequence.
55 The algorithm describes an incremental bucket sort and naturally
56 supports tunneling or massive changes of the cut-off. From an
57 conceptional point of view, it is a stripped down version of the
58 lift and drop variant without the lifts and the drops. After all,
59 each lift becomes directly an additional to the global sieve list,
62 So, in theory, we could basically take UpdateParticleGridAssociation_LiftDrop
63 and remove the lifts and drops. However, this is slow.
66 ## Impact on particle storage
68 Whenever we remove a particle from the mesh and add it to the global sieve
69 list or whenever we actually sieve a particle, i.e. add it to a vertex, we
70 alter the respective vertex. However, we never ever alter the vertex lists
71 from a coarser vertex. If this action set becomes a preamble to a gather,
72 we hence always maintain continuous memory segments in line with
73 @ref toolbox_particles_memorypool "Peano's memory pool discussion".
76 ## Sieve set administration
78 For bucket sort, each action set clones its own sieve set and then inserts
79 all particles, i.e. both inside and virtual ones. This is important, as we
80 want to have everything in the right place in one mesh sweep. We cannot
81 just insert local particles (as in other action sets) and then rely on the
82 boundary exchange to get the halos right. As each
83 action set clones its own particle set, we can in return remove any
84 particle successfully inserted.
86 This means we have some high overhead initially to replicate all sieved
87 particles. From thereon, we however run embarassingly parallel and bring
88 the memory footprint successively down.
92 def __init__(self, particle_set, guard="true"):
93 super(UpdateParticleGridAssociation_BucketSort, self).
__init__(
99 Takes those particles which are not within h/2 reach of a vertex
103 __Template_LiftParticles = jinja2.Template(
105 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_BucketSort.py
107 auto p = fineGridVertex{{PARTICLES_CONTAINER}}.begin();
108 while (p!=fineGridVertex{{PARTICLES_CONTAINER}}.end()) {
110 toolbox::particles::ParticleReassociationInstruction instruction = toolbox::particles::liftParticleAssociatedWithVertex(**p, marker);
113 int partID = (*p)->getPartid();
118 switch ( instruction ) {
119 case toolbox::particles::ParticleReassociationInstruction_Keep:
120 if (toolbox::particles::particleWillBeDroppedFurther(**p,marker)) {
121 _numberOfLiftsIntoSieveSet++;
122 logDebug( "touchVertexLastTime(...)", "lift particle " << (*p)->toString() << " locally, as it has to be dropped to next level in next iteration" );
123 toolbox::particles::assignmentchecks::detachParticleFromVertex(
127 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
131 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
133 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
137 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
140 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
142 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
148 case toolbox::particles::ParticleReassociationInstruction_SieveGlobally:
159 _numberOfLiftsIntoSieveSet++;
160 logDebug( "touchVertexLastTime(...)", "lift particle " << (*p)->toString() << " globally. Previously assigned to " << marker.toString() );
161 toolbox::particles::assignmentchecks::detachParticleFromVertex(
165 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
169 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
171 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
175 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
178 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
180 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
181 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
184 assertionMsg( false, "value not implemented" );
194 The sieve here differs from the default one in the lift-drop variants. It
195 inserts also remote particles, i.e. directly into the halo layer. It also
196 does not eliminate particles from the sieve set.
199 _Template_Sieve = jinja2.Template(
201 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_BucketSort.py
203 if ( _sieveParticleSet.hasParticlesToBeSievedIntoVertices() ) {
204 auto sievedParticles = _sieveParticleSet.getParticlesToBeSievedIntoVertex(
206 true, // removeReturnedParticlesFromSet,
207 false, // onlyReturnParticlesThatWillBeLocal
208 false // don't lock particle set, as we work with our thread-local copy
211 for (auto& sievedParticle: sievedParticles) {
212 _numberOfDropsFromSieveSet++;
213 fineGridVertex{{PARTICLES_CONTAINER}}.mergeWithParticle(
214 new globaldata::{{PARTICLE}}(*sievedParticle),
220 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
230 Core actions triggered when we run through the mesh
232 See class description. If you compare this function to
233 UpdateParticleGridAssociation_LiftDrop.get_body_of_operation(), we see
234 that we use our modified lift routine when particles have moved and in
235 return can omit the dropping. That is, the cousin's realisation is
237 ~~~~~~~~~~~~~~~~~~~~~~~~~~
238 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
239 result = self._Template_Drop.render(**self.d)
240 result += self._Template_Sieve.render(**self.d)
241 ~~~~~~~~~~~~~~~~~~~~~~~~~~
243 while we only do the sieve here.
247 if operation_name == ActionSet.OPERATION_BEGIN_TRAVERSAL:
249 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
251 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
253 if operation_name == ActionSet.OPERATION_DESTROY_PERSISTENT_VERTEX:
255 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
260 return __name__.replace(
".py",
"").replace(
".",
"_")
267 _sieveParticleSet = vertexdata::{{PARTICLES_CONTAINER}}::cloneParticlesToBeSieved();
277 _sieveParticleSet.deleteParticles();
284 super(UpdateParticleGridAssociation_BucketSort, self).
get_attributes()
287 toolbox::particles::SieveParticles< globaldata::{{PARTICLE}} > _sieveParticleSet;
Action set (reactions to events)
Associate particles to spacetree vertices.
_Template_LiftAllParticles
Associate particles to spacetree vertices.
get_body_of_operation(self, operation_name)
Core actions triggered when we run through the mesh.
get_constructor_body(self)
Define a tailored constructor body.
get_attributes(self)
Return attributes as copied and pasted into the generated class.
get_destructor_body(self)
__init__(self, particle_set, guard="true")
get_action_set_name(self)
Return unique action set name.