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.x(),
139 marker.h(),
140 _spacetreeId,
141 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
142 );
143 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
144 }
145 else {
146 p++;
147 }
148 break;
149 case toolbox::particles::ParticleReassociationInstruction_SieveGlobally:
150 case 0:
151 case 1:
152 case 2:
153 case 3:
154 #if Dimensions==3
155 case 4:
156 case 5:
157 case 6:
158 case 7:
159 #endif
160 _numberOfLiftsIntoSieveSet++;
161 logDebug( "touchVertexLastTime(...)", "lift particle " << (*p)->toString() << " globally. Previously assigned to " << marker.toString() );
162 toolbox::particles::assignmentchecks::detachParticleFromVertex(
163 "{{PARTICLE}}",
164 (*p)->getX(),
165 partID,
166 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
167 marker.x(),
168 marker.h(),
169 _spacetreeId,
170 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
171 );
172 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
173 "{{PARTICLE}}",
174 (*p)->getX(),
175 partID,
176 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
177 marker.x(),
178 marker.h(),
179 _spacetreeId,
180 "UpdateParticleGridAssociation_BucketSort::__Template_LiftParticles"
181 );
182 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
183 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
184 break;
185 default:
186 assertionMsg( false, "value not implemented" );
187 break;
188 }
189 }
190 // template end
191"""
192 )
193
194 """!
195
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.
199
200 """
201 _Template_Sieve = jinja2.Template(
202 """
203 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_BucketSort.py
204
205 if ( _sieveParticleSet.hasParticlesToBeSievedIntoVertices() ) {
206 auto sievedParticles = _sieveParticleSet.getParticlesToBeSievedIntoVertex(
207 marker,
208 true, // removeReturnedParticlesFromSet,
209 false, // onlyReturnParticlesThatWillBeLocal
210 false // don't lock particle set, as we work with our thread-local copy
211 );
212
213 for (auto& sievedParticle: sievedParticles) {
214 _numberOfDropsFromSieveSet++;
215 fineGridVertex{{PARTICLES_CONTAINER}}.mergeWithParticle(
216 new globaldata::{{PARTICLE}}(*sievedParticle),
217 marker,
218 _spacetreeId
219 );
220 }
221
222 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
223
224 // template end
225 }
226"""
227 )
228
229 def get_body_of_operation(self, operation_name):
230 """!
231
232 Core actions triggered when we run through the mesh
233
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
238
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 ~~~~~~~~~~~~~~~~~~~~~~~~~~
244
245 while we only do the sieve here.
246
247 """
248 result = "\n"
249 if operation_name == ActionSet.OPERATION_BEGIN_TRAVERSAL:
250 result = self._Template_BeginTraversal.render(**self.dd)
251 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
252 result = self._Template_Sieve.render(**self.dd)
253 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
254 result = self.__Template_LiftParticles.render(**self.dd)
255 if operation_name == ActionSet.OPERATION_DESTROY_PERSISTENT_VERTEX:
256 result = self._Template_LiftAllParticles.render(**self.dd)
257 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
258 result = self._Template_EndTraversal.render(**self.dd)
259 return result
260
262 return __name__.replace(".py", "").replace(".", "_")
263
265 return (
266 super(UpdateParticleGridAssociation_BucketSort, self).get_constructor_body()
267 + jinja2.Template(
268 """
269 _sieveParticleSet = vertexdata::{{PARTICLES_CONTAINER}}::cloneParticlesToBeSieved();
270 """
271 ).render(**self.dd)
272 )
273
275 return (
276 super(UpdateParticleGridAssociation_BucketSort, self).get_destructor_body()
277 + jinja2.Template(
278 """
279 _sieveParticleSet.deleteParticles();
280 """
281 ).render(**self.dd)
282 )
283
284 def get_attributes(self):
285 return (
286 super(UpdateParticleGridAssociation_BucketSort, self).get_attributes()
287 + jinja2.Template(
288 """
289 toolbox::particles::SieveParticles< globaldata::{{PARTICLE}} > _sieveParticleSet;
290"""
291 ).render(**self.dd)
292 )
Action set (reactions to events)
Definition ActionSet.py:6