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,
141 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
143 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
149 case toolbox::particles::ParticleReassociationInstruction_SieveGlobally:
160 _numberOfLiftsIntoSieveSet++;
161 logDebug( "touchVertexLastTime(...)", "lift particle " << (*p)->toString() << " globally. Previously assigned to " << marker.toString() );
162 toolbox::particles::assignmentchecks::detachParticleFromVertex(
166 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
170 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
172 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
176 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
180 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
182 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
183 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
186 assertionMsg( false, "value not implemented" );
196 The sieve here differs from the default one in the lift-drop variants. It
197 inserts also remote particles, i.e. directly into the halo layer. It also
198 does not eliminate particles from the sieve set.
201 _Template_Sieve = jinja2.Template(
203 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_BucketSort.py
205 if ( _sieveParticleSet.hasParticlesToBeSievedIntoVertices() ) {
206 auto sievedParticles = _sieveParticleSet.getParticlesToBeSievedIntoVertex(
208 true, // removeReturnedParticlesFromSet,
209 false, // onlyReturnParticlesThatWillBeLocal
210 false // don't lock particle set, as we work with our thread-local copy
213 for (auto& sievedParticle: sievedParticles) {
214 _numberOfDropsFromSieveSet++;
215 fineGridVertex{{PARTICLES_CONTAINER}}.mergeWithParticle(
216 new globaldata::{{PARTICLE}}(*sievedParticle),
222 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
232 Core actions triggered when we run through the mesh
234 See class description. If you compare this function to
235 UpdateParticleGridAssociation_LiftDrop.get_body_of_operation(), we see
236 that we use our modified lift routine when particles have moved and in
237 return can omit the dropping. That is, the cousin's realisation is
239 ~~~~~~~~~~~~~~~~~~~~~~~~~~
240 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
241 result = self._Template_Drop.render(**self.d)
242 result += self._Template_Sieve.render(**self.d)
243 ~~~~~~~~~~~~~~~~~~~~~~~~~~
245 while we only do the sieve here.
249 if operation_name == ActionSet.OPERATION_BEGIN_TRAVERSAL:
251 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
253 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
255 if operation_name == ActionSet.OPERATION_DESTROY_PERSISTENT_VERTEX:
257 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
262 return __name__.replace(
".py",
"").replace(
".",
"_")
269 _sieveParticleSet = vertexdata::{{PARTICLES_CONTAINER}}::cloneParticlesToBeSieved();
279 _sieveParticleSet.deleteParticles();
286 super(UpdateParticleGridAssociation_BucketSort, self).
get_attributes()
289 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.