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 auto& numberOfCoalescedActiveParticlesPerVertex = _numberOfActiveParticlesPerVertex;
208
209 _numberOfActiveParticlesAdded.push_back(0);
210 for (int i=0; i<TwoPowerD; i++) {
211 // Keep track of number of particles
212 _numberOfActiveParticlesPerVertex.push_back( fineGridVertices{{ACTIVE_PARTICLES_CONTAINER}}(i).size() );
213 _numberOfActiveParticlesAdded.back() += fineGridVertices{{ACTIVE_PARTICLES_CONTAINER}}(i).size();
214 numberOfCoalescedLocalParticlesPerVertex[i] = -1;
215 numberOfCoalescedActiveParticlesPerVertex[i] = -1;
216
217 // Add particles to active particle set
218 for (auto* p: fineGridVertices{{ACTIVE_PARTICLES_CONTAINER}}(i) ) {
219 _activeParticles.push_back( p );
220 }
221
222 // Construct the two local particle sets
223 for (auto* p: fineGridVertices{{LOCAL_PARTICLES_CONTAINER}}(i) ) {
224 bool append = marker.isContained( p->getX() );
225 if (append) {
226 localParticles.push_back( p );
227 }
228 particlesAssociatedWithLocalVertices.push_back( p );
229 }
230 }
231
232 logDebug( "touchCellFirstTime(...)", "size of local/active particles=" << localParticles.size() << "/" << _activeParticles.size() << " at " << marker.toString() );
233
234 {% if PARTICLE_CELL_UPDATE_KERNEL!=None %}
235 std::list< globaldata::{{ACTIVE_PARTICLE}}* >& activeParticles = _activeParticles;
236 {{PARTICLE_PARTICLE_INTERACTION_KERNEL}}
237 {% endif %}
238"""
239 )
240
241 __Template_TouchCellLastTime = jinja2.Template(
242 """
243 assertion( not _numberOfActiveParticlesAdded.empty() );
244 assertion( _activeParticles.size()>=_numberOfActiveParticlesAdded.back() );
245 assertion( _numberOfActiveParticlesPerVertex.size()>=TwoPowerD );
246
247
248 // More elegant way to write
249 // -------------------------
250 // for (int i=0; i<_numberOfActiveParticlesAdded.back(); i++) {
251 // _activeParticles.pop_back();
252 //}
253 _activeParticles.erase(
254 std::prev(_activeParticles.end(),_numberOfActiveParticlesAdded.back() ),
255 _activeParticles.end()
256 );
257
258 _numberOfActiveParticlesPerVertex.erase(
259 std::prev(_numberOfActiveParticlesPerVertex.end(),TwoPowerD ),
260 _numberOfActiveParticlesPerVertex.end()
261 );
262
263 _numberOfActiveParticlesAdded.pop_back();
264"""
265 )
266
267 __Template_EndTraversal = jinja2.Template(
268 """
269 assertion1( _activeParticles.empty(), (*_activeParticles.begin())->toString() );
270"""
271 )
272
273 def get_body_of_operation(self, operation_name):
274 result = "\n"
275 # if operation_name==ActionSet.OPERATION_BEGIN_TRAVERSAL:
276 # result = self.__Template_BeginTraversal.render(**self.d)
277 if operation_name == ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME:
278 result = self.__Template_TouchCellFirstTime.render(**self.d)
279 if operation_name == ActionSet.OPERATION_TOUCH_CELL_LAST_TIME:
280 result = self.__Template_TouchCellLastTime.render(**self.d)
281 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
282 result = self.__Template_TouchVertexFirstTime.render(**self.d)
283 if operation_name == ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME:
284 result = self.__Template_TouchVertexLastTime.render(**self.d)
285 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
286 result = self.__Template_EndTraversal.render(**self.d)
287 return result
288
290 return " return std::vector< peano4::grid::GridControlEvent >();\n"
291
293 return self.d["PREPARE_TRAVERSAL_KERNEL"]
294
296 return self.d["UNPREPARE_TRAVERSAL_KERNEL"]
297
299 return __name__.replace(".py", "").replace(".", "_")
300
302 return False
303
304 def get_includes(self):
305 result = jinja2.Template(
306 """
307#include "tarch/multicore/Lock.h"
308#include "toolbox/particles/particles.h"
309#include "vertexdata/{{LOCAL_PARTICLES_CONTAINER}}.h"
310#include "globaldata/{{LOCAL_PARTICLE}}.h"
311#include "vertexdata/{{ACTIVE_PARTICLES_CONTAINER}}.h"
312#include "globaldata/{{ACTIVE_PARTICLE}}.h"
313
314{{ADDITIONAL_INCLUDES}}
315
316#include <list>
317#include <vector>
318"""
319 )
320 return result.render(**self.d)
321
322 def get_attributes(self):
323 result = jinja2.Template(
324 """
325 std::list< globaldata::{{ACTIVE_PARTICLE}}* > _activeParticles;
326 std::vector< int > _numberOfActiveParticlesAdded;
327 std::vector< int > _numberOfActiveParticlesPerVertex;
328 int _spacetreeId;
329"""
330 )
331 return result.render(**self.d)
332
334 return """
335 _spacetreeId = treeNumber;
336 """
Default superclass for any data model in Peano which is stored within the grid.
Definition DaStGen2.py:47
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.