Peano
Loading...
Searching...
No Matches
TaskGraphKernelWrappers.py
Go to the documentation of this file.
1# This file is part of the ExaParty project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import jinja2
4import peano4.api
6
7
8def construct_touch_vertex_first_time_call(
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 ExaParty 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 ExaParty 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.api.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 ::exaparty::TaskNumber taskNumberToBeUsed{
92 fineGridVertex{{MARKER_NAME}}.getNumber(),
93 ::exaparty::TaskNumber::TaskAssociation::TouchVertexFirstTime
94 };
95
96 {% if USE_MULTILEVEL_DEPENDENCIES %}
97 std::set<::exaparty::TaskNumber> inDependencies = ::exaparty::getVertexNumbersOfParentVertices(
98 marker,
99 coarseGridVertices{{MARKER_NAME}},
100 ::exaparty::TaskNumber::TaskAssociation::TouchVertexFirstTime
101 );
102 {% else %}
103 std::set<::exaparty::TaskNumber> inDependencies;
104 {% endif %}
105
106 logDebug(
107 "touchVertexFirstTime(...)",
108 "create task " << taskNumberToBeUsed.toString() <<
109 " (" << ::exaparty::flatten(taskNumberToBeUsed) << ") " <<
110 " for " << marker.toString() <<
111 " depends on " << ::exaparty::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, ::exaparty::flatten(inDependencies), ::exaparty::flatten(taskNumberToBeUsed) );
130"""
131 ).render(**d)
132
133
134def construct_touch_cell_first_time_call(
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.api.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 ::exaparty::TaskNumber taskNumberToBeUsed{
172 fineGridCell{{MARKER_NAME}}.getNumber(),
173 ::exaparty::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<::exaparty::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( ::exaparty::TaskNumber{
184 fineGridVertices{{MARKER_NAME}}(i).getNumber(),
185 ::exaparty::TaskNumber::TaskAssociation::TouchVertexFirstTime
186 } );
187 }
188
189 {% if USE_MULTILEVEL_DEPENDENCIES %}
190 const ::exaparty::TaskNumber parentTaskNumber = ::exaparty::TaskNumber(
191 coarseGridCell{{MARKER_NAME}}.getNumber(),
192 ::exaparty::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 " << ::exaparty::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 ::exaparty::TaskEnumerator::lockResources(adjacentResourcesIndices);
211
212 {% if TOUCH_CELL_FIRST_TIME_KERNEL!=None %}
213 {{TOUCH_CELL_FIRST_TIME_KERNEL}}
214 {% endif %}
215
216 ::exaparty::markAllParticlesAsUpdatedWithinCell( localParticles, marker );
217
218 ::exaparty::TaskEnumerator::unlockResources(adjacentResourcesIndices);
219
220 return false;
221 }
222 #if PeanoDebug>0
223 , "cell:" + marker.toString()
224 #endif
225 );
226 tarch::multicore::spawnTask( newTask, ::exaparty::flatten(adjacentVertexAndParentTasks), ::exaparty::flatten(taskNumberToBeUsed) );
227 """
228 ).render(**d)
229
230
231
232def construct_touch_vertex_last_time_call(
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 @param alter_particle_position_or_interaction_radius: Boolean
255 If set, we force the generated code to wait for this vertex's task to
256 terminate right away, as the position/topology change has to be
257 visible before the mesh can safely continue.
258 @see exaparty.api.particle.AlgorithmStep.AlgorithmStep.may_trigger_rerun()
259
260 The tricky part in this routine is the identification of fine grid
261 dependencies. We don't have access to the fine grid data at this
262 point anymore. So what we have to do instead is memorising the
263 dependencies that will be established from fine to coarse on the
264 finer level. We use
265
266 std::set<::exaparty::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
267
268 to find out the tasks of the up to @f$ 2^d @f$ parent tasks. Those
269 are not yet spawned, as their touchVertexLastTime() will come later
270 when we ascend in the tree. Therefore, we memorise that we have to
271 add these later as in-dependencies in the underlying action set's
272 _pendingDependencies attribute.
273
274
275 ## Capture rules of lambda
276
277 Please consult the documentation of construct_touch_vertex_first_time_call().
278
279 """
280 d = {
282 current_species_set.name
283 ),
284 "USE_MULTILEVEL_DEPENDENCIES": use_multilevel_dependencies,
285 "ALTER_PARTICLE_POSITION_OR_INTERACTION_RADIUS": alter_particle_position_or_interaction_radius,
286 "TOUCH_VERTEX_LAST_TIME_KERNEL": touch_vertex_last_time_kernel,
287 "SPECIES_SET_NAME": current_species_set.name,
288 "SPECIES_SET_TASK_MARKER_NAME": peano4.api.toolbox.api.EnumerateCellsAndVertices.construct_marker_name(current_species_set.name)
289 }
290 return jinja2.Template(
291 """
292 std::set<::exaparty::TaskNumber> inDependencies;
293
294 assertion1(
295 fineGridVertex{{MARKER_NAME}}.getNumber()>=0,
296 fineGridVertex{{MARKER_NAME}}.toString()
297 );
298
299 const ::exaparty::TaskNumber taskNumberToBeUsed{
300 fineGridVertex{{MARKER_NAME}}.getNumber(),
301 ::exaparty::TaskNumber::TaskAssociation::TouchVertexLastTime
302 };
303
304 // handle finer levels
305 {% if USE_MULTILEVEL_DEPENDENCIES %}
306 inDependencies = ::exaparty::getDependenciesForTask(
307 taskNumberToBeUsed,
308 _pendingDependencies
309 );
310 {% endif %}
311
312 // add tasks from neighbour cells
313 for (int i=0; i<TwoPowerD; i++) {
314 inDependencies.insert( ::exaparty::TaskNumber{
315 fineGridVertex{{MARKER_NAME}}.getAdjacentCellNumber(i),
316 ::exaparty::TaskNumber::TaskAssociation::TouchCellFirstTime
317 } );
318 }
319
320 // - for multiscale algorithms, there's the finer indices, which might add to the counter and hence weaken the assertion
321 // - there is at least one proper adjacent cell, i.e. a local one with a valid counter
322 // - the other ones might all be undef, so they enter the set once; or they might be proper indices and increase the count
323 assertion2( inDependencies.size()>=2, marker.toString(), ::exaparty::toString(inDependencies) );
324
325 logDebug(
326 "touchVertexLastTime(...)",
327 "create task " << taskNumberToBeUsed.toString() <<
328 " for " << marker.toString() <<
329 " depends on " << ::exaparty::toString(inDependencies) <<
330 " with " << assignedParticles.size() << " particles on marker " << marker.toString()
331 );
332
333 tarch::multicore::Task* newTask = new tarch::multicore::TaskWithCopyOfFunctor (
334 tarch::multicore::Task::DontFuse,
335 tarch::multicore::Task::DefaultPriority,
336 [=,marker=marker,_spacetreeId=_spacetreeId,assignedParticles=fineGridVertexHydroPartSet,numberOfCoalescedAssignedParticles=numberOfCoalescedAssignedParticles]()->bool {
337 {% if TOUCH_VERTEX_LAST_TIME_KERNEL!=None %}
338 {{TOUCH_VERTEX_LAST_TIME_KERNEL}}
339 {% endif %}
340 return false;
341 }
342 #if PeanoDebug>0
343 , "touch-vertex-last-time:" + marker.toString()
344 #endif
345 );
346
347 tarch::multicore::spawnTask( newTask, ::exaparty::flatten(inDependencies), ::exaparty::flatten(taskNumberToBeUsed) );
348
349 {% if USE_MULTILEVEL_DEPENDENCIES %}
350 std::set<::exaparty::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
351 marker,
352 coarseGridVertices{{SPECIES_SET_TASK_MARKER_NAME}},
353 ::exaparty::TaskNumber::TaskAssociation::TouchVertexLastTime
354 );
355 for (auto p: parentTasks) {
356 _pendingDependencies.insert( std::pair<::exaparty::TaskNumber, ::exaparty::TaskNumber>(
357 taskNumberToBeUsed, p
358 ));
359 }
360 {% endif %}
361
362 {% if ALTER_PARTICLE_POSITION_OR_INTERACTION_RADIUS %}
363 constexpr bool alterParticlePositionOrInteractionRadius = true;
364 {% else %}
365 constexpr bool alterParticlePositionOrInteractionRadius = false;
366 {% endif %}
367
368 if ( marker.isAdjacentToParallelDomainBoundary() or alterParticlePositionOrInteractionRadius ) {
369 tarch::multicore::waitForTask( taskNumberToBeUsed.flatten() );
370 logDebug(
371 "touchVertexLastTime(...)",
372 "task " << taskNumberToBeUsed.toString() <<
373 " has terminated"
374 );
375 }
376 """
377 ).render(**d)