Peano
Loading...
Searching...
No Matches
UpdateParticleGridAssociation_BucketSort.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
3from peano4.solversteps.ActionSet import ActionSet
4
5
6import jinja2
7
8
9from .AbstractUpdateParticleGridAssociation import AbstractUpdateParticleGridAssociation
10
11
13 """!
14
15 Associate particles to spacetree vertices
16
17 Sort particles into the respective levels and cells. This mapping should
18 be used every time you move particles around.
19
20
21 ## Algorithm
22
23 The present algorithm is a bucket sort. It consists of very few, simple
24 steps:
25
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
32 to a global list.
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
35 closest vertex.
36
37
38 ## Dynamic adaptive mesh refinement
39
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
44 not using them here.
45
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.
51
52
53 ## Contextualisation
54
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,
60 and then we are done.
61
62 So, in theory, we could basically take UpdateParticleGridAssociation_LiftDrop
63 and remove the lifts and drops. However, this is slow.
64
65
66 ## Impact on particle storage
67
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".
74
75
76 ## Sieve set administration
77
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.
85
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.
89
90 """
91
92 def __init__(self, particle_set, guard="true"):
93 super(UpdateParticleGridAssociation_BucketSort, self).__init__(
94 particle_set, guard
95 )
96
97 """
98
99 Takes those particles which are not within h/2 reach of a vertex
100 and lift those.
101
102 """
103 __Template_LiftParticles = jinja2.Template(
104 """
105 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_BucketSort.py
106
107 auto p = fineGridVertex{{PARTICLES_CONTAINER}}.begin();
108 while (p!=fineGridVertex{{PARTICLES_CONTAINER}}.end()) {
109
110 toolbox::particles::ParticleReassociationInstruction instruction = toolbox::particles::liftParticleAssociatedWithVertex(**p, marker);
111
112 #if PeanoDebug > 0
113 int partID = (*p)->getPartid();
114 #else
115 int partID = -1;
116 #endif
117
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(
124 "{{PARTICLE}}",
125 (*p)->getX(),
126 partID,
127 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
128 marker.x(),
129 marker.h(),
130 _spacetreeId,
131 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
132 );
133 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
134 "{{PARTICLE}}",
135 (*p)->getX(),
136 partID,
137 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
138 marker.h(),
139 _spacetreeId,
140 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
141 );
142 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
143 }
144 else {
145 p++;
146 }
147 break;
148 case toolbox::particles::ParticleReassociationInstruction_SieveGlobally:
149 case 0:
150 case 1:
151 case 2:
152 case 3:
153 #if Dimensions==3
154 case 4:
155 case 5:
156 case 6:
157 case 7:
158 #endif
159 _numberOfLiftsIntoSieveSet++;
160 logDebug( "touchVertexLastTime(...)", "lift particle " << (*p)->toString() << " globally. Previously assigned to " << marker.toString() );
161 toolbox::particles::assignmentchecks::detachParticleFromVertex(
162 "{{PARTICLE}}",
163 (*p)->getX(),
164 partID,
165 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
166 marker.x(),
167 marker.h(),
168 _spacetreeId,
169 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
170 );
171 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
172 "{{PARTICLE}}",
173 (*p)->getX(),
174 partID,
175 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
176 marker.h(),
177 _spacetreeId,
178 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
179 );
180 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
181 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
182 break;
183 default:
184 assertionMsg( false, "value not implemented" );
185 break;
186 }
187 }
188 // template end
189"""
190 )
191
192 """!
193
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.
197
198 """
199 _Template_Sieve = jinja2.Template(
200 """
201 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_BucketSort.py
202
203 if ( _sieveParticleSet.hasParticlesToBeSievedIntoVertices() ) {
204 auto sievedParticles = _sieveParticleSet.getParticlesToBeSievedIntoVertex(
205 marker,
206 true, // removeReturnedParticlesFromSet,
207 false, // onlyReturnParticlesThatWillBeLocal
208 false // don't lock particle set, as we work with our thread-local copy
209 );
210
211 for (auto& sievedParticle: sievedParticles) {
212 _numberOfDropsFromSieveSet++;
213 fineGridVertex{{PARTICLES_CONTAINER}}.mergeWithParticle(
214 new globaldata::{{PARTICLE}}(*sievedParticle),
215 marker,
216 _spacetreeId
217 );
218 }
219
220 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
221
222 // template end
223 }
224"""
225 )
226
227 def get_body_of_operation(self, operation_name):
228 """!
229
230 Core actions triggered when we run through the mesh
231
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
236
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 ~~~~~~~~~~~~~~~~~~~~~~~~~~
242
243 while we only do the sieve here.
244
245 """
246 result = "\n"
247 if operation_name == ActionSet.OPERATION_BEGIN_TRAVERSAL:
248 result = self._Template_BeginTraversal.render(**self.dd)
249 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
250 result = self._Template_Sieve.render(**self.dd)
251 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
252 result = self.__Template_LiftParticles.render(**self.dd)
253 if operation_name == ActionSet.OPERATION_DESTROY_PERSISTENT_VERTEX:
254 result = self._Template_LiftAllParticles.render(**self.dd)
255 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
256 result = self._Template_EndTraversal.render(**self.dd)
257 return result
258
260 return __name__.replace(".py", "").replace(".", "_")
261
263 return (
264 super(UpdateParticleGridAssociation_BucketSort, self).get_constructor_body()
265 + jinja2.Template(
266 """
267 _sieveParticleSet = vertexdata::{{PARTICLES_CONTAINER}}::cloneParticlesToBeSieved();
268 """
269 ).render(**self.dd)
270 )
271
273 return (
274 super(UpdateParticleGridAssociation_BucketSort, self).get_destructor_body()
275 + jinja2.Template(
276 """
277 _sieveParticleSet.deleteParticles();
278 """
279 ).render(**self.dd)
280 )
281
282 def get_attributes(self):
283 return (
284 super(UpdateParticleGridAssociation_BucketSort, self).get_attributes()
285 + jinja2.Template(
286 """
287 toolbox::particles::SieveParticles< globaldata::{{PARTICLE}} > _sieveParticleSet;
288"""
289 ).render(**self.dd)
290 )
Action set (reactions to events)
Definition ActionSet.py:6