8def construct_touch_vertex_first_time_call(
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 ExaParty 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 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
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 ::exaparty::TaskNumber taskNumberToBeUsed{
92 fineGridVertex{{MARKER_NAME}}.getNumber(),
93 ::exaparty::TaskNumber::TaskAssociation::TouchVertexFirstTime
96 {% if USE_MULTILEVEL_DEPENDENCIES %}
97 std::set<::exaparty::TaskNumber> inDependencies = ::exaparty::getVertexNumbersOfParentVertices(
99 coarseGridVertices{{MARKER_NAME}},
100 ::exaparty::TaskNumber::TaskAssociation::TouchVertexFirstTime
103 std::set<::exaparty::TaskNumber> inDependencies;
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()
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, ::exaparty::flatten(inDependencies), ::exaparty::flatten(taskNumberToBeUsed) );
134def construct_touch_cell_first_time_call(
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 ::exaparty::TaskNumber taskNumberToBeUsed{
172 fineGridCell{{MARKER_NAME}}.getNumber(),
173 ::exaparty::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<::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
189 {% if USE_MULTILEVEL_DEPENDENCIES %}
190 const ::exaparty::TaskNumber parentTaskNumber = ::exaparty::TaskNumber(
191 coarseGridCell{{MARKER_NAME}}.getNumber(),
192 ::exaparty::TaskNumber::TaskAssociation::TouchCellFirstTime
194 adjacentVertexAndParentTasks.insert( parentTaskNumber );
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
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);
212 {% if TOUCH_CELL_FIRST_TIME_KERNEL!=None %}
213 {{TOUCH_CELL_FIRST_TIME_KERNEL}}
216 ::exaparty::markAllParticlesAsUpdatedWithinCell( localParticles, marker );
218 ::exaparty::TaskEnumerator::unlockResources(adjacentResourcesIndices);
223 , "cell:" + marker.toString()
226 tarch::multicore::spawnTask( newTask, ::exaparty::flatten(adjacentVertexAndParentTasks), ::exaparty::flatten(taskNumberToBeUsed) );
232def construct_touch_vertex_last_time_call(
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 @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()
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
266 std::set<::exaparty::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
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.
275 ## Capture rules of lambda
277 Please consult the documentation of construct_touch_vertex_first_time_call().
282 current_species_set.name
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,
290 return jinja2.Template(
292 std::set<::exaparty::TaskNumber> inDependencies;
295 fineGridVertex{{MARKER_NAME}}.getNumber()>=0,
296 fineGridVertex{{MARKER_NAME}}.toString()
299 const ::exaparty::TaskNumber taskNumberToBeUsed{
300 fineGridVertex{{MARKER_NAME}}.getNumber(),
301 ::exaparty::TaskNumber::TaskAssociation::TouchVertexLastTime
304 // handle finer levels
305 {% if USE_MULTILEVEL_DEPENDENCIES %}
306 inDependencies = ::exaparty::getDependenciesForTask(
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
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) );
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()
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}}
343 , "touch-vertex-last-time:" + marker.toString()
347 tarch::multicore::spawnTask( newTask, ::exaparty::flatten(inDependencies), ::exaparty::flatten(taskNumberToBeUsed) );
349 {% if USE_MULTILEVEL_DEPENDENCIES %}
350 std::set<::exaparty::TaskNumber> parentTasks = getVertexNumbersOfParentVertices(
352 coarseGridVertices{{SPECIES_SET_TASK_MARKER_NAME}},
353 ::exaparty::TaskNumber::TaskAssociation::TouchVertexLastTime
355 for (auto p: parentTasks) {
356 _pendingDependencies.insert( std::pair<::exaparty::TaskNumber, ::exaparty::TaskNumber>(
357 taskNumberToBeUsed, p
362 {% if ALTER_PARTICLE_POSITION_OR_INTERACTION_RADIUS %}
363 constexpr bool alterParticlePositionOrInteractionRadius = true;
365 constexpr bool alterParticlePositionOrInteractionRadius = false;
368 if ( marker.isAdjacentToParallelDomainBoundary() or alterParticlePositionOrInteractionRadius ) {
369 tarch::multicore::waitForTask( taskNumberToBeUsed.flatten() );
371 "touchVertexLastTime(...)",
372 "task " << taskNumberToBeUsed.toString() <<