Peano
Loading...
Searching...
No Matches
UpdateParticleGridAssociation_LiftDrop.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 re-association has different ingredients. We have two implementations
24 currently available. The present version does not touch any particle within
25 the cell events. It realises the following steps/checks:
26
27 - If I touch a vertex for the last time, I check if its particles are
28 within h/2 Manhattan distance. If not, I lift them to the next coarser
29 level.
30 - Drop particles down within the tree hierarchy such that they reside on
31 an as fine level as possible. This happens in touchVertexFirstTime(), i.e.
32 in-between two grid traversals, particles are distributed over all
33 resolutions, but we move them down in the hierarchy as kind of a preamble
34 of the subsequent grid sweep.
35
36 This variant is algorithmically simpler than other ones, but it moves particles
37 around more often: If a particle moves closer to another vertex than the
38 one it is currently associated, we do not directly reassign it to a new
39 vertex. Instead, we move it one level up within the tree. Just before the
40 subsequent grid sweep, I sieve the particles all down again. So the move
41 from one vertex to the next is realised via a move through the next coarser
42 level. The first variant does not move particles up and down that often, but
43 it checks particles more often, as each particle is checked from up to 2^d
44 adjacent cells. So it is not clear which variant is superior.
45
46
47 ## Tunneling and horizontal data movements between trees
48
49 All cores run through their trees. On the finest mesh level, we have a
50 non-overlapping domain decomposition. Each cell has a unique owner tree.
51 On the coarser resolutions, the association is not unique anymore. We
52 make it unique, i.e. each cell on any level has a unique owner. The owners
53 (aka threads or ranks) run through their trees concurrently and exchange
54 their boundary data after the traversal. If particles are to be lifted, this
55 causes issues, as we cannot exchange them between two levels after the
56 traversal. We could, but then we can't lift them any further within the
57 first traversal, and we also cannot drop them into another rank at the
58 same time.
59
60 Therefore, I employ a mixture of the pidt technique from the paper and the
61 sieve approach also discussed there. Usually, I do all lifts and drops
62 right throughout the mesh traversal. If I'd lift into a cell/level which
63 is owned by another tree, I don't lift but instead dump the particle into
64 a rank-global list. These lists are then exchanged after each traversal
65 sweep. Drops now can either happen from the next coarser level or this
66 global list. I call the latter a sieve.
67
68 Due to this global list approach, I can support tunneling, i.e. particles
69 racing through multiple cells in one time step, and all of this works with
70 MPI, too.
71
72
73 ## Statistics
74
75 The mapping keeps some statistics on any state update. These statistics
76 however are not shared via MPI or synchronised between different threads.
77 It is the job of the ParticleSet to ensure that we have a proper global
78 bookkeeping.
79
80
81 ## Where and how to use
82
83 Within your algorithm step, this update action set should always be the
84 first or one of the first action sets before you add any other action set.
85 If you add it first, it ensures that the drop of particles is one of the
86 first things that happen, before any user code is triggered. As Peano
87 automatically inverts the action set order throughout the backtracking,
88 adding UpdateParticleGridAssociation as first action set ensures that the
89 lifts are basically the last thing you do. This is particular imporant if
90 your user code alters particle positions in touchVertexLastTime().
91
92 Your main code has to call finishedTraversal() on the tracer set after each
93 sweep on each rank. This important to roll those particles over that have
94 been sieved or have been lifted.
95
96 You will always needs to instances of this action set in a row, as the first
97 one might lift particles and put them into a sieve list. After that, the
98 subsequent grid sweep will drop the particles and sieve them in again. As
99 long as particles do not move, this variant becomes idempotent after two
100 mesh sweeps.
101
102
103 ## Hanging vertices
104
105 Hanging vertices do never hold information and therefore are never assigned
106 any particles.
107
108
109 ## Interpolay with particle storage
110
111 This sorting scheme should not be used if you use a particle memory pool,
112 i.e. if you try to store particles continuously - unless you insert
113 explicit gather/resort steps. Read through the @ref toolbox_particles_memorypool "memory implications for any particle sorting".
114
115
116 """
117
118 def __init__(self, particle_set, guard="true"):
119 super(UpdateParticleGridAssociation_LiftDrop, self).__init__(
120 particle_set, guard
121 )
122
123 _Template_Sieve = jinja2.Template(
124 """
125 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_LiftDrop.py
126
127 if ( vertexdata::{{PARTICLES_CONTAINER}}::hasParticlesToBeSievedIntoVertices() ) {
128 auto sievedParticles = vertexdata::{{PARTICLES_CONTAINER}}::getParticlesToBeSievedIntoVertex(
129 marker
130 );
131
132 for (auto& newParticle: sievedParticles) {
133 _numberOfDropsFromSieveSet++;
134 fineGridVertex{{PARTICLES_CONTAINER}}.mergeWithParticle( newParticle, marker, _spacetreeId);
135 }
136
137 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
138 }
139
140 // end template
141"""
142 )
143
144 """!
145
146 Takes those particles which are not within h/2 reach of a vertex and lift those.
147
148 This code snippet is used with touchVertexLastTime(). Therefore, the marker
149 is a vertex marker.
150
151 @see toolbox::particles::updateContainedInAdjacentCellProperty()
152
153 """
154 __Template_LiftOrReassignParticles = jinja2.Template(
155 """
156 // template in python/peano4/toolbox/particles/api/UpdateParticleGridAssociation_LiftDrop.py
157
158 auto p = fineGridVertex{{PARTICLES_CONTAINER}}.begin();
159 while (p!=fineGridVertex{{PARTICLES_CONTAINER}}.end()) {
160
161 toolbox::particles::ParticleReassociationInstruction instruction = toolbox::particles::liftParticleAssociatedWithVertex(**p, marker);
162
163 #if PeanoDebug > 0
164 int partID = (*p)->getPartid();
165 #else
166 int partID = -1;
167 #endif
168 switch ( instruction ) {
169 case toolbox::particles::ParticleReassociationInstruction_Keep:
170 toolbox::particles::updateContainedInAdjacentCellProperty(
171 marker,
172 **p
173 );
174 p++;
175 break;
176 case toolbox::particles::ParticleReassociationInstruction_SieveGlobally:
177 _numberOfLiftsIntoSieveSet++;
178 logDebug( "touchVertexLastTime(...)", "have to lift particle " << (*p)->toString() << " on tree " << _spacetreeId << " globally. Previously assigned to " << marker.toString() );
179
180 toolbox::particles::assignmentchecks::detachParticleFromVertex(
181 "{{PARTICLE}}",
182 (*p)->getX(),
183 partID,
184 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
185 marker.x(),
186 marker.h(),
187 _spacetreeId,
188 "UpdateParticleGridAssociation_LiftDrop::__Template_LiftOrReassignParticles"
189 );
190 toolbox::particles::assignmentchecks::assignParticleToSieveSet(
191 "{{PARTICLE}}",
192 (*p)->getX(),
193 partID,
194 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
195 marker.x(),
196 marker.h(),
197 _spacetreeId,
198 "UpdateParticleGridAssociation_LiftDrop::__Template_LiftOrReassignParticles"
199 );
200
201 p = fineGridVertex{{PARTICLES_CONTAINER}}.particleCanNotBeLiftedLocally(p);
202 assertion1( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString() );
203 break;
204 case 0:
205 case 1:
206 case 2:
207 case 3:
208 #if Dimensions==3
209 case 4:
210 case 5:
211 case 6:
212 case 7:
213 #endif
214 _numberOfLifts ++;
215 logDebug( "touchVertexLastTime(...)", "have to lift particle " << (*p)->toString() << " to next coarser vertex " << instruction << ". Previously assigned to " << marker.toString() << ". Now lifted to " << coarseGridVertices{{PARTICLES_CONTAINER}}( instruction ).toString() );
216 (*p)->setCellH(3.0 * marker.h());
217 toolbox::particles::updateContainedInAdjacentCellProperty(
218 peano4::datamanagement::reconstructXOfParentVertex(marker, instruction),
219 marker.h()*3.0,
220 **p
221 );
222 toolbox::particles::assignmentchecks::detachParticleFromVertex(
223 "{{PARTICLE}}",
224 (*p)->getX(),
225 partID,
226 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
227 marker.x(),
228 marker.h(),
229 _spacetreeId,
230 "UpdateParticleGridAssociation_LiftDrop::__Template_LiftOrReassignParticles"
231 );
232 toolbox::particles::assignmentchecks::assignParticleToVertex(
233 "{{PARTICLE}}",
234 (*p)->getX(),
235 partID,
236 (*p)->getParallelState()==globaldata::{{PARTICLE}}::ParallelState::Local,
237 peano4::datamanagement::reconstructXOfParentVertex(marker, instruction),
238 3.0*marker.h(),
239 _spacetreeId,
240 "UpdateParticleGridAssociation_LiftDrop::__Template_LiftOrReassignParticles",
241 marker.x(),
242 marker.h()
243 );
244 p = coarseGridVertices{{PARTICLES_CONTAINER}}( instruction ).grabParticle( p, fineGridVertex{{PARTICLES_CONTAINER}} );
245 assertion2( fineGridVertex{{PARTICLES_CONTAINER}}.isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString(), coarseGridVertices{{PARTICLES_CONTAINER}}( instruction ).toString() );
246 assertion2( coarseGridVertices{{PARTICLES_CONTAINER}}( instruction ).isValid(), fineGridVertex{{PARTICLES_CONTAINER}}.toString(), coarseGridVertices{{PARTICLES_CONTAINER}}( instruction ).toString() );
247 break;
248 default:
249 assertionMsg( false, "value not implemented" );
250 break;
251 }
252 }
253
254 // end template
255"""
256 )
257
258 def get_body_of_operation(self, operation_name):
259 """!
260
261 Core algorithm. See class description.
262
263 """
264 result = "\n"
265 if operation_name == ActionSet.OPERATION_BEGIN_TRAVERSAL:
266 result = self._Template_BeginTraversal.render(**self.d)
267 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
268 result = self._Template_Drop.render(**self.d)
269 result += self._Template_Sieve.render(**self.d)
270 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
271 result = self.__Template_LiftOrReassignParticles.render(**self.d)
272 if operation_name == ActionSet.OPERATION_DESTROY_PERSISTENT_VERTEX:
273 result = self._Template_LiftAllParticles.render(**self.d)
274 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
275 result = self._Template_EndTraversal.render(**self.d)
276 return result
277
279 return __name__.replace(".py", "").replace(".", "_")
Action set (reactions to events)
Definition ActionSet.py:6