Peano
Loading...
Searching...
No Matches
TaskGraphKernelWrappers.py
Go to the documentation of this file.
1# This file is part of the SWIFT2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import jinja2
4import peano4
6
7
9 current_species_set,
10 touch_vertex_first_time_kernel,
11 use_multilevel_dependencies,
12):
13 """!
14
15 Take the kernel to be used in touch first time and wrap into tasks
16
17 As we work with task dependencies, I decided to always spawn tasks, even if
18 these degenerate to empty ones as no particles are associated to a vertex.
19 Otherwise, it just became a nightmare.
20 Notably note that the kernel is not empty in most cases, as it is enriched
21 with Swift dependency tracking anyway.
22
23
24 ## Multiscale task order
25
26 In terms of multiscale dependencies, we impose the following order:
27
28 - touchVertexFirstTime(): Touch all associated particles for the first
29 time. At this point, touchVertexFirstTime() on corresponding coarser
30 levels has to be complete.
31 - touchCellFirstTime(): Handle all particles within the cell. At this point
32 the corresponding first touch of every particle has to be finished.
33 - Recurse
34 - touchVertexLastTime(): At this point, touchVertexLastTime() on all finer
35 levels has to be complete.
36
37
38 ## Realisation
39
40 - Get the task number to be used (corresponding to TouchVertexFirstTime).
41 - Allocate the set of in dependencies from coarser level if a multiscale
42 algorithm is used.
43 - Issue task.
44
45 If we build up one task graph per algorithmic step, there's no particularly
46 interesting details left: The underlying observer with its action sets will
47 yield a touch first-vertex first-vertex first-cell-vertex last-vertex last
48 sequence. The last "vertex last" event will be followed by a global
49 waitForAllTasks() call, and therefore we are ready to for the subsequent
50 mesh sweep. All the "vertex first" calls will use the same task number.
51 The second "vertex first" task therefore will have an in-out dependency
52 on this number.
53
54 If we roll around, i.e. build up a task graph over multiple mesh sweeps, no
55 such strategy will work, as we have to insert a "vertex last"-"vertex first"
56 dependency in-between any two sweeps. This fact is ignored here and left
57 to the user. Indeed, consult MultisweepTaskGraph for details on how they
58 handle these wrap-around dependencies.
59
60
61 ## Capture rules of lambda
62
63 The task bodies as injected by Swift work with the object's attributes and
64 also with references. Both things are not really copied by a capture
65 = clause: The references are copied, but still point to the same, temporary
66 object. The accesses to object attributes are internally mapped onto
67 this->attribute and hence the pointer this is copied, but not the attribute
68 itself.
69
70 All of the rules are detailed in
71 https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture.
72
73 Therefore, we have to explicitly copy the task-relevant attributes, and we
74 even use an explicit initialiser to do so.
75
76 """
77 d = {
79 current_species_set.name
80 ),
81 "USE_MULTILEVEL_DEPENDENCIES": use_multilevel_dependencies,
82 "TOUCH_VERTEX_FIRST_TIME_KERNEL": touch_vertex_first_time_kernel,
83 "SPECIES_SET_TASK_MARKER_NAME": peano4.toolbox.api.EnumerateCellsAndVertices.construct_marker_name(current_species_set.name)
84 }
85 return jinja2.Template(
86 """
87 assertion1(
88 fineGridVertex{{MARKER_NAME}}.getNumber()>=0,
89 fineGridVertex{{MARKER_NAME}}.toString()
90 );
91 const ::swift2::TaskNumber taskNumberToBeUsed{
92 fineGridVertex{{MARKER_NAME}}.getNumber(),
93 ::swift2::TaskNumber::TaskAssociation::TouchVertexFirstTime
94 };
95
96 {% if USE_MULTILEVEL_DEPENDENCIES %}
97 std::set<::swift2::TaskNumber> inDependencies = ::swift2::getVertexNumbersOfParentVertices(
98 marker,
99 coarseGridVertices{{MARKER_NAME}},
100 ::swift2::TaskNumber::TaskAssociation::TouchVertexFirstTime
101 );
102 {% else %}
103 std::set<::swift2::TaskNumber> inDependencies;
104 {% endif %}
105
106 logDebug(
107 "touchVertexFirstTime(...)",
108 "create task " << taskNumberToBeUsed.toString() <<
109 " (" << ::swift2::flatten(taskNumberToBeUsed) << ") " <<
110 " for " << marker.toString() <<
111 " depends on " << ::swift2::toString(inDependencies) <<
112 " with " << assignedParticles.size() << " particles on marker " << marker.toString()
113 );
114
115 tarch::multicore::Task* newTask = new tarch::multicore::TaskWithCopyOfFunctor (
116 tarch::multicore::Task::DontFuse,
117 tarch::multicore::Task::DefaultPriority,
118 [=,marker=marker,_spacetreeId=_spacetreeId,assignedParticles=fineGridVertexHydroPartSet,numberOfCoalescedAssignedParticles=numberOfCoalescedAssignedParticles]()->bool {
119 {% if TOUCH_VERTEX_FIRST_TIME_KERNEL!=None %}
120 {{TOUCH_VERTEX_FIRST_TIME_KERNEL}}
121 {% endif %}
122 return false;
123 }
124 #if PeanoDebug>0
125 , "touch-vertex-first-time:" + marker.toString()
126 #endif
127 );
128
129 tarch::multicore::spawnTask( newTask, ::swift2::flatten(inDependencies), ::swift2::flatten(taskNumberToBeUsed) );
130"""
131 ).render(**d)
132
133
135 current_species_set,
136 touch_cell_first_time_kernel,
137 use_multilevel_dependencies,
138):
139 """!
140
141 Wrap cell kernel into task
142
143 Consult the documentation of construct_touch_vertex_first_time_call()
144 for a discussion of the task orders.
145
146 In principle, the handling cells is straightforward: We take the @f$ 2^d @f$ adjacent
147 vertices and exatract their task numbers, and then we also add an
148 additional task number for the parent cell if we have multi-level
149 dependencies.
150
151
152 ## Capture rules of lambda
153
154 Please consult the documentation of construct_touch_vertex_first_time_call().
155
156 """
157 d = {
159 current_species_set.name
160 ),
161 "USE_MULTILEVEL_DEPENDENCIES": use_multilevel_dependencies,
162 "TOUCH_CELL_FIRST_TIME_KERNEL": touch_cell_first_time_kernel,
163 "SPECIES_SET_TASK_MARKER_NAME": peano4.toolbox.api.EnumerateCellsAndVertices.construct_marker_name(current_species_set.name)
164 }
165 return jinja2.Template(
166 """
167 assertion1(
168 fineGridCell{{MARKER_NAME}}.getNumber()>=0,
169 fineGridCell{{MARKER_NAME}}.toString()
170 );
171 const ::swift2::TaskNumber taskNumberToBeUsed{
172 fineGridCell{{MARKER_NAME}}.getNumber(),
173 ::swift2::TaskNumber::TaskAssociation::TouchCellFirstTime
174 };
175
176 // I originally used [TwoPowerD], but this then is a pointer. Albeit on
177 // the stack, it will not be copied by the lambda capture below.
178 tarch::la::Vector<TwoPowerD,int> adjacentResourcesIndices;
179 std::set<::swift2::TaskNumber> adjacentVertexAndParentTasks;
180 for (int i=0; i<TwoPowerD; i++) {
181 adjacentResourcesIndices[i] = fineGridVertices{{MARKER_NAME}}(i).getNumber();
182 assertion3(adjacentResourcesIndices[i]==tarch::Enumerator::NoNumber or adjacentResourcesIndices[i]>=0, adjacentResourcesIndices[i], i, marker.toString() );
183 adjacentVertexAndParentTasks.insert( ::swift2::TaskNumber{
184 fineGridVertices{{MARKER_NAME}}(i).getNumber(),
185 ::swift2::TaskNumber::TaskAssociation::TouchVertexFirstTime
186 } );
187 }
188
189 {% if USE_MULTILEVEL_DEPENDENCIES %}
190 const ::swift2::TaskNumber parentTaskNumber = ::swift2::TaskNumber(
191 coarseGridCell{{MARKER_NAME}}.getNumber(),
192 ::swift2::TaskNumber::TaskAssociation::TouchCellFirstTime
193 );
194 adjacentVertexAndParentTasks.insert( parentTaskNumber );
195 {% endif %}
196
197 logDebug(
198 "touchCellFirstTime(...)",
199 "create task " << taskNumberToBeUsed.toString() <<
200 " for " << marker.toString() <<
201 " depends on " << ::swift2::toString(adjacentVertexAndParentTasks) <<
202 " with " << localParticles.size() << " local particles on marker " << marker.toString() <<
203 " on tree " << _spacetreeId
204 );
205
206 tarch::multicore::Task* newTask = new tarch::multicore::TaskWithCopyOfFunctor (
207 tarch::multicore::Task::DontFuse,
208 tarch::multicore::Task::DefaultPriority,
209 [=,marker=marker,_spacetreeId=_spacetreeId,localParticles=localParticles,numberOfCoalescedLocalParticlesPerVertex=numberOfCoalescedLocalParticlesPerVertex,activeParticles = _activeParticles,numberOfCoalescedActiveParticlesPerVertex = _numberOfActiveParticlesPerVertex,adjacentResourcesIndices=adjacentResourcesIndices]()->bool {
210 ::swift2::TaskEnumerator::lockResources(adjacentResourcesIndices);
211
212 {% if TOUCH_CELL_FIRST_TIME_KERNEL!=None %}
213 {{TOUCH_CELL_FIRST_TIME_KERNEL}}
214 {% endif %}
215
216 ::swift2::markAllParticlesAsUpdatedWithinCell( localParticles, marker );
217
218 ::swift2::TaskEnumerator::unlockResources(adjacentResourcesIndices);
219
220 return false;
221 }
222 #if PeanoDebug>0
223 , "cell:" + marker.toString()
224 #endif
225 );
226 tarch::multicore::spawnTask( newTask, ::swift2::flatten(adjacentVertexAndParentTasks), ::swift2::flatten(taskNumberToBeUsed) );
227 """
228 ).render(**d)
229
230
231
233 current_species_set,
234 touch_vertex_last_time_kernel,
235 use_multilevel_dependencies,
236 alter_particle_position_or_interaction_radius,
237):
238 """!
239
240 Wrap vertex kernel into task
241
242 Consult the documentation of construct_touch_vertex_first_time_call()
243 for a discussion of the task orders.
244 Multiple things have to be taken into account here:
245
246 - We add dependencies to all @f$ 2^d @f$ adjacent cells. Their tasks
247 have to be completed before we start our work. This is notably
248 important if we are on the finest mesh level, i.e. the marker is
249 not refined. But it never hurts to add these dependendices.
250 - We then add dependencies for finer levels (see discussion below).
251
252 Once these two types of dependencies are in, we can spawn the task.
253
254 The tricky part in this routine is the identification of fine grid
255 dependencies. We don't have access to the fine grid data at this
256 point anymore. So what we have to do instead is memorising the
257 dependencies that will be established from fine to coarse on the
258 finer level. We use
259
260 std::set<::swift2::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
261
262 to find out the tasks of the up to @f$ 2^d @f$ parent tasks. Those
263 are not yet spawned, as their touchVertexLastTime() will come later
264 when we ascend in the tree. Therefore, we memorise that we have to
265 add these later as in-dependencies in the underlying action set's
266 _pendingDependencies attribute.
267
268
269 ## Capture rules of lambda
270
271 Please consult the documentation of construct_touch_vertex_first_time_call().
272
273 """
274 d = {
276 current_species_set.name
277 ),
278 "USE_MULTILEVEL_DEPENDENCIES": use_multilevel_dependencies,
279 "ALTER_PARTICLE_POSITION_OR_INTERACTION_RADIUS": alter_particle_position_or_interaction_radius,
280 "TOUCH_VERTEX_LAST_TIME_KERNEL": touch_vertex_last_time_kernel,
281 "SPECIES_SET_NAME": current_species_set.name,
282 "SPECIES_SET_TASK_MARKER_NAME": peano4.toolbox.api.EnumerateCellsAndVertices.construct_marker_name(current_species_set.name)
283 }
284 return jinja2.Template(
285 """
286 std::set<::swift2::TaskNumber> inDependencies;
287
288 assertion1(
289 fineGridVertex{{MARKER_NAME}}.getNumber()>=0,
290 fineGridVertex{{MARKER_NAME}}.toString()
291 );
292
293 const ::swift2::TaskNumber taskNumberToBeUsed{
294 fineGridVertex{{MARKER_NAME}}.getNumber(),
295 ::swift2::TaskNumber::TaskAssociation::TouchVertexLastTime
296 };
297
298 // handle finer levels
299 {% if USE_MULTILEVEL_DEPENDENCIES %}
300 inDependencies = ::swift2::getDependenciesForTask(
301 taskNumberToBeUsed,
302 _pendingDependencies
303 );
304 {% endif %}
305
306 // add tasks from neighbour cells
307 for (int i=0; i<TwoPowerD; i++) {
308 inDependencies.insert( ::swift2::TaskNumber{
309 fineGridVertex{{MARKER_NAME}}.getAdjacentCellNumber(i),
310 ::swift2::TaskNumber::TaskAssociation::TouchCellFirstTime
311 } );
312 }
313
314 // - for multiscale algorithms, there's the finer indices, which might add to the counter and hence weaken the assertion
315 // - there is at least one proper adjacent cell, i.e. a local one with a valid counter
316 // - the other ones might all be undef, so they enter the set once; or they might be proper indices and increase the count
317 assertion2( inDependencies.size()>=2, marker.toString(), ::swift2::toString(inDependencies) );
318
319 logDebug(
320 "touchVertexLastTime(...)",
321 "create task " << taskNumberToBeUsed.toString() <<
322 " for " << marker.toString() <<
323 " depends on " << ::swift2::toString(inDependencies) <<
324 " with " << assignedParticles.size() << " particles on marker " << marker.toString()
325 );
326
327 tarch::multicore::Task* newTask = new tarch::multicore::TaskWithCopyOfFunctor (
328 tarch::multicore::Task::DontFuse,
329 tarch::multicore::Task::DefaultPriority,
330 [=,marker=marker,_spacetreeId=_spacetreeId,assignedParticles=fineGridVertexHydroPartSet,numberOfCoalescedAssignedParticles=numberOfCoalescedAssignedParticles]()->bool {
331 {% if TOUCH_VERTEX_LAST_TIME_KERNEL!=None %}
332 {{TOUCH_VERTEX_LAST_TIME_KERNEL}}
333 {% endif %}
334 return false;
335 }
336 #if PeanoDebug>0
337 , "touch-vertex-last-time:" + marker.toString()
338 #endif
339 );
340
341 tarch::multicore::spawnTask( newTask, ::swift2::flatten(inDependencies), ::swift2::flatten(taskNumberToBeUsed) );
342
343 {% if USE_MULTILEVEL_DEPENDENCIES %}
344 std::set<::swift2::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
345 marker,
346 coarseGridVertices{{SPECIES_SET_TASK_MARKER_NAME}},
347 ::swift2::TaskNumber::TaskAssociation::TouchVertexLastTime
348 );
349 for (auto p: parentTasks) {
350 _pendingDependencies.insert( std::pair<::swift2::TaskNumber, ::swift2::TaskNumber>(
351 taskNumberToBeUsed, p
352 ));
353 }
354 {% endif %}
355
356 {% if ALTER_PARTICLE_POSITION_OR_INTERACTION_RADIUS %}
357 constexpr bool alterParticlePositionOrInteractionRadius = true;
358 {% else %}
359 constexpr bool alterParticlePositionOrInteractionRadius = false;
360 {% endif %}
361
362 if ( marker.isAdjacentToParallelDomainBoundary() or alterParticlePositionOrInteractionRadius ) {
363 tarch::multicore::waitForTask( taskNumberToBeUsed.flatten() );
364 logDebug(
365 "touchVertexLastTime(...)",
366 "task " << taskNumberToBeUsed.toString() <<
367 " has terminated"
368 );
369 }
370 """
371 ).render(**d)
construct_touch_cell_first_time_call(current_species_set, touch_cell_first_time_kernel, use_multilevel_dependencies)
Wrap cell kernel into task.
construct_touch_vertex_last_time_call(current_species_set, touch_vertex_last_time_kernel, use_multilevel_dependencies, alter_particle_position_or_interaction_radius)
Wrap vertex kernel into task.
construct_touch_vertex_first_time_call(current_species_set, touch_vertex_first_time_kernel, use_multilevel_dependencies)
Take the kernel to be used in touch first time and wrap into tasks.