10 touch_vertex_first_time_kernel,
11 use_multilevel_dependencies,
15 Take the kernel to be used in touch first time and wrap into tasks
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.
24 ## Multiscale task order
26 In terms of multiscale dependencies, we impose the following order:
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.
34 - touchVertexLastTime(): At this point, touchVertexLastTime() on all finer
35 levels has to be complete.
40 - Get the task number to be used (corresponding to TouchVertexFirstTime).
41 - Allocate the set of in dependencies from coarser level if a multiscale
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
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.
61 ## Capture rules of lambda
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
70 All of the rules are detailed in
71 https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture.
73 Therefore, we have to explicitly copy the task-relevant attributes, and we
74 even use an explicit initialiser to do so.
79 current_species_set.name
81 "USE_MULTILEVEL_DEPENDENCIES": use_multilevel_dependencies,
82 "TOUCH_VERTEX_FIRST_TIME_KERNEL": touch_vertex_first_time_kernel,
85 return jinja2.Template(
88 fineGridVertex{{MARKER_NAME}}.getNumber()>=0,
89 fineGridVertex{{MARKER_NAME}}.toString()
91 const ::swift2::TaskNumber taskNumberToBeUsed{
92 fineGridVertex{{MARKER_NAME}}.getNumber(),
93 ::swift2::TaskNumber::TaskAssociation::TouchVertexFirstTime
96 {% if USE_MULTILEVEL_DEPENDENCIES %}
97 std::set<::swift2::TaskNumber> inDependencies = ::swift2::getVertexNumbersOfParentVertices(
99 coarseGridVertices{{MARKER_NAME}},
100 ::swift2::TaskNumber::TaskAssociation::TouchVertexFirstTime
103 std::set<::swift2::TaskNumber> inDependencies;
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()
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}}
125 , "touch-vertex-first-time:" + marker.toString()
129 tarch::multicore::spawnTask( newTask, ::swift2::flatten(inDependencies), ::swift2::flatten(taskNumberToBeUsed) );
136 touch_cell_first_time_kernel,
137 use_multilevel_dependencies,
141 Wrap cell kernel into task
143 Consult the documentation of construct_touch_vertex_first_time_call()
144 for a discussion of the task orders.
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
152 ## Capture rules of lambda
154 Please consult the documentation of construct_touch_vertex_first_time_call().
159 current_species_set.name
161 "USE_MULTILEVEL_DEPENDENCIES": use_multilevel_dependencies,
162 "TOUCH_CELL_FIRST_TIME_KERNEL": touch_cell_first_time_kernel,
165 return jinja2.Template(
168 fineGridCell{{MARKER_NAME}}.getNumber()>=0,
169 fineGridCell{{MARKER_NAME}}.toString()
171 const ::swift2::TaskNumber taskNumberToBeUsed{
172 fineGridCell{{MARKER_NAME}}.getNumber(),
173 ::swift2::TaskNumber::TaskAssociation::TouchCellFirstTime
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
189 {% if USE_MULTILEVEL_DEPENDENCIES %}
190 const ::swift2::TaskNumber parentTaskNumber = ::swift2::TaskNumber(
191 coarseGridCell{{MARKER_NAME}}.getNumber(),
192 ::swift2::TaskNumber::TaskAssociation::TouchCellFirstTime
194 adjacentVertexAndParentTasks.insert( parentTaskNumber );
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
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);
212 {% if TOUCH_CELL_FIRST_TIME_KERNEL!=None %}
213 {{TOUCH_CELL_FIRST_TIME_KERNEL}}
216 ::swift2::markAllParticlesAsUpdatedWithinCell( localParticles, marker );
218 ::swift2::TaskEnumerator::unlockResources(adjacentResourcesIndices);
223 , "cell:" + marker.toString()
226 tarch::multicore::spawnTask( newTask, ::swift2::flatten(adjacentVertexAndParentTasks), ::swift2::flatten(taskNumberToBeUsed) );
234 touch_vertex_last_time_kernel,
235 use_multilevel_dependencies,
236 alter_particle_position_or_interaction_radius,
240 Wrap vertex kernel into task
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:
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).
252 Once these two types of dependencies are in, we can spawn the task.
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
260 std::set<::swift2::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
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.
269 ## Capture rules of lambda
271 Please consult the documentation of construct_touch_vertex_first_time_call().
276 current_species_set.name
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,
284 return jinja2.Template(
286 std::set<::swift2::TaskNumber> inDependencies;
289 fineGridVertex{{MARKER_NAME}}.getNumber()>=0,
290 fineGridVertex{{MARKER_NAME}}.toString()
293 const ::swift2::TaskNumber taskNumberToBeUsed{
294 fineGridVertex{{MARKER_NAME}}.getNumber(),
295 ::swift2::TaskNumber::TaskAssociation::TouchVertexLastTime
298 // handle finer levels
299 {% if USE_MULTILEVEL_DEPENDENCIES %}
300 inDependencies = ::swift2::getDependenciesForTask(
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
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) );
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()
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}}
337 , "touch-vertex-last-time:" + marker.toString()
341 tarch::multicore::spawnTask( newTask, ::swift2::flatten(inDependencies), ::swift2::flatten(taskNumberToBeUsed) );
343 {% if USE_MULTILEVEL_DEPENDENCIES %}
344 std::set<::swift2::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
346 coarseGridVertices{{SPECIES_SET_TASK_MARKER_NAME}},
347 ::swift2::TaskNumber::TaskAssociation::TouchVertexLastTime
349 for (auto p: parentTasks) {
350 _pendingDependencies.insert( std::pair<::swift2::TaskNumber, ::swift2::TaskNumber>(
351 taskNumberToBeUsed, p
356 {% if ALTER_PARTICLE_POSITION_OR_INTERACTION_RADIUS %}
357 constexpr bool alterParticlePositionOrInteractionRadius = true;
359 constexpr bool alterParticlePositionOrInteractionRadius = false;
362 if ( marker.isAdjacentToParallelDomainBoundary() or alterParticlePositionOrInteractionRadius ) {
363 tarch::multicore::waitForTask( taskNumberToBeUsed.flatten() );
365 "touchVertexLastTime(...)",
366 "task " << taskNumberToBeUsed.toString() <<