Peano
Loading...
Searching...
No Matches
UpdateParticle_MultiLevelInteraction_StackOfLists.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
5import jinja2
6
8
10
11
13 """!
14
15 Tree walker to realise particle-particle interactions with particles held by different adjacent cells or on different mesh levels
16
17 This code snippet creates a tree walker, i.e. an action set that runs
18 through the underlying spacetree top down and builds up
19 @ref page_toolbox_particles_mesh_storage "the active set and the local set".
20 These sets can then be used to trigger mesh events.
21
22 This version of the interactions makes no assumptions on how the particles
23 are stored. Hence, it cannot expose any such information. If you want to
24 offer information about consecutive storage, e.g., please use
25 UpdateParticle_MultiLevelInteraction_StackOfLists_ContiguousParticles in combination
26 with @ref toolbox_particles_memorypool "the memory pool".
27
28
29 The code is slightly more sophisticated compared to the set-based tree
30 traversal approach, as it exposes more information:
31
32
33 - ***marker*** The CellMarker is avaialable to a compute kernel and allows you
34 to query spatial information.
35 - ***activeParticles*** The list of active particles is a list of pointers. It
36 follows the @ref page_toolbox_particles_mesh_storage "generic definition of active sets".
37 - ***localParticles*** This is a list of local particles, i.e. particles within
38 the cell which are tied to the cell's adjacent vertices. While they reside on
39 the cell's level, we cannot be sure that they are not also local to a
40 neighbouring octant within the spacetree, as we work with floating point
41 arithmetics and hence have no unique particle-cell assocation. In any case,
42 marker.isContained() holds for all particles within this set.
43 - ***particlesAssociatedWithLocalVertices*** This additional list holds all
44 particles that are associated with adjacent vertices of the current cell.
45 This is a superset of localParticles and also holds particles for which
46 marker.isContained() does not hold.
47 - ***_numberOfActiveParticlesPerVertex*** is a sequence of integers
48 which tells you how many particles within activeParticles are added by one
49 particular vertex.
50 - ***numberOfLocalParticlesPerVertex*** is a sequence of integers which
51 tells you how many particles within particlesAssociatedWithLocalVertices are
52 added by each adjacent vertex.
53
54
55
56
57 ## Different particles species
58
59 Both active and local set are of the same type if you stick
60 to the default value of None for active_particle_set. But you can also
61 let particles of different type interact by selecting a local particle
62 set that is different to the active one. Users can inject code to modify
63 the local set per cell and hence to alter particle states.
64
65
66 ## Realisation and data consistency
67
68 This tree walker realises the two sets via two lists: Per cell, we
69 append the local set to the active set and memorise on a stack how
70 many of these particles have been added. When we backtrace, i.e.
71 ascend within the tree again, this memorised size is used to remove
72 the last n elements from the active set.
73
74 So the active set grows while we descend in the tree, and it is
75 truncated when we ascend again.
76
77 This is a very efficient implementation yet fails if particles move
78 up and down within the tree while we walk. It should work perfectly
79 fine if particles do not move. If particles move up and down, the
80 present algorithms might have redundant pointers within the active set.
81 I am also not 100% sure if we might run into memory issues if particles
82 suddenly leave cells and hence "disappear" from the active sets.
83
84 In the case of a dynamic setup, i.e. in combination with the action
85 set UpdateParticleGridAssociation, you might be better of with using
86 the set-based implementation of the particle-particle interaction.
87
88
89 ## Further plug-in points
90
91 Besides the cell kernel which is there to realise particle-to-particle
92 interactions, we also have a vertex kernel which we call whenever a
93 vertex is loaded for the first time. That is, you can assume that the
94 vertex kernel has been launched for all @f$ 2^d @f$ vertices of a cell before
95 its cell kernel is triggered. The vertex kernel is also passed on the
96 active set (from the coarser level). Its local set is all the particles
97 whose centre lies within the square/cube around the vertex with mesh size
98 h. So this area goes h/2 along each each coordinate axis into the
99 neighbouring cells.
100
101 """
102
104 self,
105 particle_set,
106 particle_particle_interaction_kernel,
107 touch_vertex_first_time_compute_particle_update_kernel=None,
108 touch_vertex_last_time_compute_particle_update_kernel=None,
109 prepare_traversal_kernel="",
110 unprepare_traversal_kernel="",
111 additional_includes="",
112 active_particle_set=None,
113 ):
114 """!
115
116 Initialise object
117
118 ## Arguments
119
120 particle_set: ParticleSet
121
122 particle_particle_interaction_kernel: String holding C++ code
123 This C++ code can access three different types of variables: There's
124 a list of particles called activeParticles, there's a list of particles
125 called localParticles, and there's the cell marker. See the guidebook
126 for further info. A typical kernel resembles
127
128 for (auto* localParticle: localParticles )
129 for (auto* activeParticle: activeParticles ) {
130 localParticle->doSomethingFancy( activeParticle );
131 }
132
133 active_particle_set: ParticleSet or None
134 You can compare different particles species with this argument. It
135 allows you to make the active particles stem from another species than
136 the local ones that you actually update. Pick None if both sets are of
137 the same type.
138
139 touch_vertex_first_time_compute_particle_update_kernel: String or None
140 Can be empty, but if you wanna move particles, then a minimal example
141 string passed equals
142
143 for (auto* localParticle: localParticles ) {
144 localParticle->setMoveState( globaldata::Particle::MoveState::NotMovedYet );
145 }
146
147 i.e. you will have a variable localParticle available in this kernel
148 and it is a pointer.
149
150
151 """
152 super(UpdateParticle_MultiLevelInteraction_StackOfLists, self).__init__(
153 descend_invocation_order=1, parallel=False
154 )
155
156 self._particle_set = particle_set
157 self.d = {}
158 self.d["LOCAL_PARTICLE"] = particle_set.particle_model.name
159 self.d["LOCAL_PARTICLES_CONTAINER"] = particle_set.name
160 if active_particle_set == None:
161 self.d["ACTIVE_PARTICLE"] = particle_set.particle_model.name
162 self.d["ACTIVE_PARTICLES_CONTAINER"] = particle_set.name
163 else:
164 self.d["ACTIVE_PARTICLE"] = active_particle_set.particle_model.name
165 self.d["ACTIVE_PARTICLES_CONTAINER"] = active_particle_set.name
166 self.d[
167 "PARTICLE_PARTICLE_INTERACTION_KERNEL"
168 ] = particle_particle_interaction_kernel
169 self.d[
170 "TOUCH_VERTEX_FIRST_COMPUTE_KERNEL"
171 ] = touch_vertex_first_time_compute_particle_update_kernel
172 self.d[
173 "TOUCH_VERTEX_LAST_COMPUTE_KERNEL"
174 ] = touch_vertex_last_time_compute_particle_update_kernel
175 self.d["ADDITIONAL_INCLUDES"] = additional_includes
176 self.d["PREPARE_TRAVERSAL_KERNEL"] = prepare_traversal_kernel
177 self.d["UNPREPARE_TRAVERSAL_KERNEL"] = unprepare_traversal_kernel
178
179 __Template_TouchVertexFirstTime = jinja2.Template(
180 """
181 {% if TOUCH_VERTEX_FIRST_COMPUTE_KERNEL!=None %}
182 auto& assignedParticles = fineGridVertex{{LOCAL_PARTICLES_CONTAINER}};
183 constexpr int numberOfCoalescedAssignedParticles = -1;
184
185 {{TOUCH_VERTEX_FIRST_COMPUTE_KERNEL}}
186 {% endif %}
187"""
188 )
189
190 __Template_TouchVertexLastTime = jinja2.Template(
191 """
192 {% if TOUCH_VERTEX_LAST_COMPUTE_KERNEL!=None %}
193 auto& assignedParticles = fineGridVertex{{LOCAL_PARTICLES_CONTAINER}};
194 constexpr int numberOfCoalescedAssignedParticles = -1;
195
196 {{TOUCH_VERTEX_LAST_COMPUTE_KERNEL}}
197 {% endif %}
198"""
199 )
200
201 __Template_TouchCellFirstTime = jinja2.Template(
202 """
203 std::list< globaldata::{{LOCAL_PARTICLE}}* > localParticles;
204 std::list< globaldata::{{LOCAL_PARTICLE}}* > particlesAssociatedWithLocalVertices;
205 std::vector< int > numberOfLocalParticlesPerVertex( TwoPowerD );
206 std::vector< int > _numberOfActiveParticlesPerVertex( TwoPowerD ); // stay compatible with coalesced version
207 std::vector< bool > localParticleIsContainedInCell;
208 auto& numberOfCoalescedActiveParticlesPerVertex = _numberOfActiveParticlesPerVertex;
209
210 _numberOfActiveParticlesAdded.push_back(0);
211 for (int i=0; i<TwoPowerD; i++) {
212 // Keep track of number of particles
213 _numberOfActiveParticlesPerVertex.push_back( fineGridVertices{{ACTIVE_PARTICLES_CONTAINER}}(i).size() );
214 _numberOfActiveParticlesAdded.back() += fineGridVertices{{ACTIVE_PARTICLES_CONTAINER}}(i).size();
215 numberOfCoalescedLocalParticlesPerVertex[i] = -1;
216 numberOfCoalescedActiveParticlesPerVertex[i] = -1;
217
218 // Add particles to active particle set
219 for (auto* p: fineGridVertices{{ACTIVE_PARTICLES_CONTAINER}}(i) ) {
220 _activeParticles.push_back( p );
221 }
222
223 // Construct the two local particle sets
224 for (auto* p: fineGridVertices{{LOCAL_PARTICLES_CONTAINER}}(i) ) {
225 std::bitset<TwoPowerD> mask = 0;
226 mask.set(TwoPowerD-i-1);
227 bool isLocal = (mask & p->getContainedInAdjacentCell()).any();
228 assertionEquals4(
229 isLocal,
230 marker.isContained( p->getX(), toolbox::particles::internal::relativeGrabOwnershipSpatialSortingTolerance( marker.h() ) * tarch::la::NUMERICAL_ZERO_DIFFERENCE ),
231 marker.toString(), p->toString(), i, mask
232 );
233 localParticleIsContainedInCell.push_back( isLocal );
234 localParticles.push_back( p );
235 particlesAssociatedWithLocalVertices.push_back( p );
236 }
237 }
238
239 logDebug( "touchCellFirstTime(...)", "size of local/active particles=" << localParticles.size() << "/" << _activeParticles.size() << " at " << marker.toString() );
240
241 {% if PARTICLE_CELL_UPDATE_KERNEL!=None %}
242 std::list< globaldata::{{ACTIVE_PARTICLE}}* >& activeParticles = _activeParticles;
243 {{PARTICLE_PARTICLE_INTERACTION_KERNEL}}
244 {% endif %}
245"""
246 )
247
248 __Template_TouchCellLastTime = jinja2.Template(
249 """
250 assertion( not _numberOfActiveParticlesAdded.empty() );
251 assertion( _activeParticles.size()>=_numberOfActiveParticlesAdded.back() );
252 assertion( _numberOfActiveParticlesPerVertex.size()>=TwoPowerD );
253
254
255 // More elegant way to write
256 // -------------------------
257 // for (int i=0; i<_numberOfActiveParticlesAdded.back(); i++) {
258 // _activeParticles.pop_back();
259 //}
260 _activeParticles.erase(
261 std::prev(_activeParticles.end(),_numberOfActiveParticlesAdded.back() ),
262 _activeParticles.end()
263 );
264
265 _numberOfActiveParticlesPerVertex.erase(
266 std::prev(_numberOfActiveParticlesPerVertex.end(),TwoPowerD ),
267 _numberOfActiveParticlesPerVertex.end()
268 );
269
270 _numberOfActiveParticlesAdded.pop_back();
271"""
272 )
273
274 __Template_EndTraversal = jinja2.Template(
275 """
276 assertion1( _activeParticles.empty(), (*_activeParticles.begin())->toString() );
277"""
278 )
279
280 def get_body_of_operation(self, operation_name):
281 result = "\n"
282 # if operation_name==ActionSet.OPERATION_BEGIN_TRAVERSAL:
283 # result = self.__Template_BeginTraversal.render(**self.d)
284 if operation_name == ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME:
285 result = self.__Template_TouchCellFirstTime.render(**self.d)
286 if operation_name == ActionSet.OPERATION_TOUCH_CELL_LAST_TIME:
287 result = self.__Template_TouchCellLastTime.render(**self.d)
288 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
289 result = self.__Template_TouchVertexFirstTime.render(**self.d)
290 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
291 result = self.__Template_TouchVertexLastTime.render(**self.d)
292 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
293 result = self.__Template_EndTraversal.render(**self.d)
294 return result
295
297 return " return std::vector< peano4::grid::GridControlEvent >();\n"
298
300 return self.d["PREPARE_TRAVERSAL_KERNEL"]
301
303 return self.d["UNPREPARE_TRAVERSAL_KERNEL"]
304
306 return __name__.replace(".py", "").replace(".", "_")
307
309 return False
310
311 def get_includes(self):
312 result = jinja2.Template(
313 """
314#include "tarch/multicore/Lock.h"
315#include "toolbox/particles/particles.h"
316#include "vertexdata/{{LOCAL_PARTICLES_CONTAINER}}.h"
317#include "globaldata/{{LOCAL_PARTICLE}}.h"
318#include "vertexdata/{{ACTIVE_PARTICLES_CONTAINER}}.h"
319#include "globaldata/{{ACTIVE_PARTICLE}}.h"
320
321{{ADDITIONAL_INCLUDES}}
322
323#include <list>
324#include <vector>
325"""
326 )
327 return result.render(**self.d)
328
329 def get_attributes(self):
330 result = jinja2.Template(
331 """
332 std::list< globaldata::{{ACTIVE_PARTICLE}}* > _activeParticles;
333 std::vector< int > _numberOfActiveParticlesAdded;
334 std::vector< int > _numberOfActiveParticlesPerVertex;
335 int _spacetreeId;
336"""
337 )
338 return result.render(**self.d)
339
341 return """
342 _spacetreeId = treeNumber;
343 """
Default superclass for any data model in Peano which is stored within the grid.
Definition DaStGen2.py:197
Action set (reactions to events)
Definition ActionSet.py:6
__init__(self, particle_set, particle_particle_interaction_kernel, touch_vertex_first_time_compute_particle_update_kernel=None, touch_vertex_last_time_compute_particle_update_kernel=None, prepare_traversal_kernel="", unprepare_traversal_kernel="", additional_includes="", active_particle_set=None)
Initialise object.