Peano
Loading...
Searching...
No Matches
CellTaskProducerConsumer.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
3import copy
4import re
5from typing import List
6
7import dastgen2
8
9import peano4.api
12
13from ..Graph import Graph
14from ..Task import Task
15from . import FuseSteps
16from . import Sequential
17from .SpawnCellTaskActionSet import SpawnCellTaskActionSet
18from .WaitForCellTaskActionSet import WaitForCellTaskActionSet
19
20
22 graph: Graph,
23 identifier: str,
24 peano_project: "peano4.api.Project",
25) -> List["peano4.api.solversteps.Step"]:
26 """!
27
28 Split tasks writing cell data into producer and consumer, and map each task onto its own mesh traversal
29
30 You can use this task graph compiler in your ExaHyPE 2 project through
31
32 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33 project.compiler = peano4.api.taskgraph.compiler.CellTaskProducerConsumer.translate_with_sequential
34 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35
36 The rewritten graph is handed over to
37 @ref peano4.api.taskgraph.compiler.Sequential.translate(). See
38 @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()
39 for a description of the rewrite and of the parameters.
40
41 """
42 return _translate(graph, identifier, peano_project, use_fusion=False)
43
44
46 graph: Graph,
47 identifier: str,
48 peano_project: "peano4.api.Project",
49) -> List["peano4.api.solversteps.Step"]:
50 """!
51
52 Split tasks writing cell data into producer and consumer, and fuse tasks into as few mesh traversals as possible
53
54 You can use this task graph compiler in your ExaHyPE 2 project through
55
56 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57 project.compiler = peano4.api.taskgraph.compiler.CellTaskProducerConsumer.translate_with_fusion
58 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59
60 The rewritten graph is handed over to
61 @ref peano4.api.taskgraph.compiler.FuseSteps.translate(), which never
62 fuses a producer with its consumer. See
63 @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()
64 for a description of the rewrite and of the parameters.
65
66 """
67 return _translate(graph, identifier, peano_project, use_fusion=True)
68
69
71 graph: Graph,
72 identifier: str,
73 peano_project: "peano4.api.Project",
74 use_fusion: bool,
75) -> List["peano4.api.solversteps.Step"]:
76 """!
77
78 Task graph compiler which splits tasks writing cell data into a producer spawning tasks and a consumer waiting for them (work in progress)
79
80 Do not use this routine directly. Pick
81 @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer.translate_with_sequential()
82 or
83 @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer.translate_with_fusion()
84 instead.
85
86 This compiler adds a preamble which rewrites the task graph, and then
87 hands the rewritten graph over to either
88 @ref peano4.api.taskgraph.compiler.Sequential.translate() or
89 @ref peano4.api.taskgraph.compiler.FuseSteps.translate().
90
91 ## Preamble
92
93 Every task which exclusively writes cell data is replaced by two tasks.
94 A task exclusively writes cell data if it declares at least one cell
95 submodel as WRITE or READ_AND_WRITE, but no face or vertex submodel as
96 WRITE or READ_AND_WRITE (@ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._exclusively_writes_cell_data()). It may
97 read face or vertex data. All other tasks are kept as they are.
98
99 A task is only split if this is safe
100 (@ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._has_events_after_touch_cell_first_time()):
101 The producer spawns tasks which might still run after the producer's
102 mesh traversal. If any action set of the task yields code for
103 unprepareTraversal(), endTraversal() or one of the touch...LastTime()
104 events, these events might be invoked before the spawned tasks have
105 terminated. Such a task is hence not split, and the compiler writes a
106 warning. You can enforce the split by setting the task's flag
107 @ref peano4.api.taskgraph.Task.Task.may_spawn_background_tasks to True,
108 which skips this check. If the flag is False, the task is never split,
109 and the compiler writes no warning.
110
111 The two tasks are:
112
113 1. The producer, named after the original task plus "Producer". It
114 inherits all action sets and data declarations of the original
115 task.
116 2. The consumer, named after the original task plus "Consumer". It
117 works on cells, too, and depends on the producer. It declares every
118 cell submodel that the producer writes (WRITE or READ_AND_WRITE) as
119 READ_AND_WRITE.
120
121 Every task that depended on the original task depends on the consumer
122 instead. Producer and consumer are marked as non-fusable, i.e. if we
123 use FuseSteps, it never puts them into the same mesh traversal. A
124 consumer however might be fused with subsequent tasks. In an ExaHyPE 2
125 setup, for example, the initial condition yields a producer and a
126 consumer, and the latter is fused with the subsequent projection onto
127 the faces.
128
129 The preamble does not alter the graph passed in. It works on a copy of
130 the graph's tasks.
131
132 ## Spawning tasks
133
134 If a peano_project is given, the producer spawns the touchCellFirstTime()
135 events of its action sets as tasks, and the consumer waits for these
136 tasks (@ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._spawn_tasks()):
137
138 - Every action set of the producer whose touchCellFirstTime() has a
139 non-empty body is wrapped into a
140 @ref peano4.api.taskgraph.compiler.SpawnCellTaskActionSet.SpawnCellTaskActionSet.
141 The wrapper evaluates the guard. If it holds, the wrapper takes a new
142 task number from the tarch, stores it in the cell's task label (see
143 below), and spawns the body as a task with this number. The task
144 depends on the task spawned by the previous wrapped action set of the
145 same producer, so the bodies are executed in their original order.
146 - Action sets whose touchCellFirstTime() has an empty body, such as
147 @ref exahype2.api.fv.actionsets.ApplyDirichletBoundaryConditions.ApplyDirichletBoundaryConditions, are not
148 part of the producer-consumer pattern. They remain plain action sets
149 of the producer, i.e. all their events are invoked by the producer.
150 - The same holds for action sets which do not want to run in a task
151 (@ref peano4.api.solversteps.ActionSet.ActionSet.may_run_in_background_task()),
152 such as
153 @ref exahype2.api.fv.actionsets.ReadjustTimeStepSizeForOptimisticGlobalTimestepping.ReadjustTimeStepSizeForOptimisticGlobalTimestepping.
154 Their touchCellFirstTime() is invoked directly by the producer, i.e.
155 possibly while tasks spawned by previous action sets of the same
156 producer still run. They hence must not depend on these tasks.
157 - The consumer is given a cousin
158 @ref peano4.api.taskgraph.compiler.WaitForCellTaskActionSet.WaitForCellTaskActionSet
159 per wrapped action set. If the cell's task label holds a valid task
160 number, it waits for this task, returns the number to the task
161 enumerator and resets the label to NoNumber=-1.
162 - All other events of the wrapped action sets remain within the
163 producer.
164
165 The spawned tasks outlive touchCellFirstTime(). The compiler therefore
166 warns about constructs which might not be safe within a task. It does
167 not inspect the code but only searches the generated C++ text for the
168 names of data and attributes, i.e. the warnings state that a task
169 might access something. Every warning names the generator which
170 realises the data in question and suggests a fix. It has to be
171 accepted or the generation is aborted
172 (@ref peano4.api.Logger.Logger.warn_and_confirm()):
173
174 - The task body might access fine or coarse grid cell data which is not
175 held via a smart pointer, e.g. via @ref peano4.api.datamodel.PatchToDoubleArray or
176 @ref peano4.api.datamodel.PatchToDoubleArrayOnHeap. The task copies
177 the data, and writes to the copy are lost. The fix is a smart-pointer
178 generator such as
179 @ref peano4.api.datamodel.PatchToDoubleArrayWithSmartPointer.
180 Heap-based generators do not suffice, as they deep-copy their data
181 whenever Peano copies an object.
182 - The task body might access vertex or face data via an enumerator
183 whose data is not held via a smart pointer. The enumerators hold
184 pointers to the objects on Peano's stacks, which are invalid once
185 touchCellFirstTime() has terminated. The task therefore always
186 captures them as peano4::datamanagement::FaceHeapDataEnumerator or
187 peano4::datamanagement::VertexHeapDataEnumerator, which copy the
188 faces or vertices. Without smart pointers, these copies are deep,
189 i.e. writes are lost. With smart pointers, no warning is issued.
190 - The task body might use a non-static attribute of the action set.
191 The task accesses an object which is destroyed after the producer's
192 mesh traversal.
193 - An action set of the producer has to be modified by the user
194 (@ref peano4.api.solversteps.ActionSet.ActionSet.user_should_modify_template()).
195 Such a producer is not altered, i.e. it spawns no tasks.
196
197 ## Task labels
198
199 The compiler adds a new cell data type <identifier>_TaskLabels to the
200 project's data model. It is modelled via DaStGen2 and holds one integer
201 per producer action set with a non-empty touchCellFirstTime(). The
202 integer's name is the CamelCase version of the original task's name
203 followed by ActionSet, the action set's index within the task and
204 TaskNumber, e.g. EulerFVSmoothInitialConditionActionSet0TaskNumber
205 (@ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._task_number_attribute_name()).
206 The labels furthermore hold a static tarch::multicore::TaskEnumerator
207 (getTaskEnumerator()), from which the producers take their task numbers
208 and to which the consumers return them. The producer declares the task
209 labels as WRITE, the consumer as READ_AND_DISCARD: The producer sets
210 the label in every sweep, whether it spawns a task or not, so the
211 consumer's reset does not have to be kept. If the consumer declared
212 READ_AND_WRITE, a fused producer step that directly follows a consumer
213 would read the labels, too
214 (@ref peano4.api.taskgraph.compiler.PropagateUseStatements), and the
215 labels would be relayed through steps of other phases which run before
216 the labels are ever stored.
217
218 @param graph: peano4.api.taskgraph.Graph
219 The task graph to compile. Assumed to be acyclic.
220
221 @param identifier: str
222 @ref peano4.api.taskgraph.compiler.FuseSteps.translate()
223
224 @param peano_project: peano4.api.Project
225 Project the task labels are added to. If None, no task labels are
226 added and the producers spawn no tasks.
227
228 @param use_fusion: bool
229 If True, hand the rewritten graph over to FuseSteps, otherwise to
230 Sequential.
231
232 @return [peano4.api.solversteps.Step]
233 @ref peano4.api.taskgraph.compiler.FuseSteps.translate()
234
235 """
236 producer_consumer_graph, split_tasks = _split_cell_tasks(graph)
237
238 if peano_project is not None and split_tasks:
239 task_labels = peano4.api.datamodel.DaStGen2(f"{identifier}_TaskLabels")
240 task_labels.data.add_attribute(
242 name="taskEnumerator",
243 type="tarch::multicore::TaskEnumerator",
244 include='#include "tarch/multicore/TaskEnumerator.h"',
245 qualifier=dastgen2.attributes.Attribute.Qualifier.STATIC,
246 user_type_has_toString_method=False,
247 )
248 )
249 if _spawn_tasks(split_tasks, task_labels) > 0:
250 peano_project.datamodel.add_cell(task_labels)
251
252 non_fusable_task_pairs = [(producer, consumer) for _, producer, consumer in split_tasks]
253 if use_fusion:
254 return FuseSteps.translate(producer_consumer_graph, identifier, peano_project, non_fusable_task_pairs)
255 else:
256 return Sequential.translate(producer_consumer_graph, identifier, peano_project)
257
258
259def _task_number_attribute_name(task: Task, action_set_index: int) -> str:
260 """!
261
262 CamelCase version of the task's name followed by ActionSet, the action
263 set index and TaskNumber. Words are separated by whitespace or
264 underscores, and only the first letter of each word is capitalised, so
265 existing camel case and acronyms are preserved. "EulerFVSmooth_Update
266 cell" with action set index 0 for example becomes
267 EulerFVSmoothUpdateCellActionSet0TaskNumber.
268
269 """
270 words = task.name.replace("_", " ").split()
271 return "".join(word[:1].upper() + word[1:] for word in words) + f"ActionSet{action_set_index}TaskNumber"
272
273
274def _copy_task(task: Task) -> Task:
275 """!
276
277 Shallow copy of a task with its own lists and dictionaries, so we can
278 alter the copy's dependencies without touching the original graph. The
279 action sets and the submodels themselves are shared.
280
281 """
282 result = copy.copy(task)
283 result.dependencies = []
284 result.action_sets = list(task.action_sets)
285 result.cell_data = list(task.cell_data)
286 result.face_data = list(task.face_data)
287 result.vertex_data = list(task.vertex_data)
288 result.cell_data_access = dict(task.cell_data_access)
289 result.face_data_access = dict(task.face_data_access)
290 result.vertex_data_access = dict(task.vertex_data_access)
291 return result
292
293
294def _exclusively_writes_cell_data(task: Task) -> bool:
295 """!
296
297 True if task declares at least one cell submodel as WRITE or
298 READ_AND_WRITE, and no face or vertex submodel as WRITE or
299 READ_AND_WRITE. @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()
300
301 """
302 writing_accesses = (Task.DataAccess.WRITE, Task.DataAccess.READ_AND_WRITE)
303 writes_cell_data = any(access in writing_accesses for access in task.cell_data_access.values())
304 writes_other_data = any(
305 access in writing_accesses
306 for access in list(task.face_data_access.values()) + list(task.vertex_data_access.values())
307 )
308 return writes_cell_data and not writes_other_data
309
310
311def _has_events_after_touch_cell_first_time(task: Task) -> List[str]:
312 """!
313
314 List the events which the task's action sets realise and which a
315 producer would invoke before its spawned tasks have necessarily
316 terminated: unprepareTraversal(), endTraversal() and the
317 touch...LastTime() events. An event counts if the action set yields
318 code for it.
319
320 @return [str]
321 One entry "<action set name>::<event>" per such event. The list is
322 empty if the task can be split safely.
323
324 """
325 late_operations = (
326 peano4.api.solversteps.ActionSet.OPERATION_END_TRAVERSAL,
327 peano4.api.solversteps.ActionSet.OPERATION_TOUCH_VERTEX_LAST_TIME,
328 peano4.api.solversteps.ActionSet.OPERATION_TOUCH_FACE_LAST_TIME,
329 peano4.api.solversteps.ActionSet.OPERATION_TOUCH_CELL_LAST_TIME,
330 )
331 result = []
332 for action_set in task.action_sets:
333 if action_set.get_implementation_of_unprepareTraversal().strip() != "":
334 result.append(f"{action_set.get_action_set_name()}::unprepareTraversal")
335 for operation_name in late_operations:
336 if action_set.get_implementation_of_operation(operation_name).strip() != "":
337 result.append(f"{action_set.get_action_set_name()}::{operation_name}")
338 return result
339
340
341def _split_cell_tasks(graph: Graph):
342 """!
343
344 Build the producer-consumer graph. @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()
345
346 @return (peano4.api.taskgraph.Graph, [(Task, Task, Task)])
347 The new graph and the list of (original task, producer, consumer)
348 triples. Producer and consumer must not be fused.
349
350 """
351 producer_of_task = {}
352 consumer_of_task = {}
353 for task in graph.tasks:
354 producer_of_task[task] = _copy_task(task)
355 if task.may_spawn_background_tasks is False or not _exclusively_writes_cell_data(task):
356 continue
358 if late_events and task.may_spawn_background_tasks is not True:
359 peano4.api.Logger().log(
360 f"Warning: Task {task.name} is not split into a producer and a consumer task, as a split-up might be unsafe: "
361 f"The producer's spawned tasks might still run when the following events are invoked: "
362 + ", ".join(late_events)
363 + f". Set may_spawn_background_tasks=True on task {task.name} to enforce the split.",
364 peano4.api.LoggerPrintOption.ALWAYS_PRINT,
365 )
366 continue
367 producer_of_task[task].name = f"{task.name} Producer"
368 consumer = Task(name=f"{task.name} Consumer", entity=Task.GridEntity.CELL)
369 for submodel in task.cell_data:
370 if task.cell_data_access[submodel] in (Task.DataAccess.WRITE, Task.DataAccess.READ_AND_WRITE):
371 consumer.use_cell(submodel, Task.DataAccess.READ_AND_WRITE)
372 consumer_of_task[task] = consumer
373
374 result = Graph()
375 for task in graph.tasks:
376 producer = producer_of_task[task]
377 for dependency in task.dependencies:
378 # Depend on whatever finishes the dependency: its consumer if
379 # it has been split, otherwise the (copied) task itself
380 producer.add_dependency(consumer_of_task.get(dependency, producer_of_task[dependency]))
381 result.add_task(producer)
382 if task in consumer_of_task:
383 consumer = consumer_of_task[task]
384 consumer.add_dependency(producer)
385 result.add_task(consumer)
386
387 split_tasks = [(task, producer_of_task[task], consumer_of_task[task]) for task in graph.tasks if task in consumer_of_task]
388 return result, split_tasks
389
390
391def _non_static_attribute_names(attributes: str) -> List[str]:
392 """!
393
394 Names of the non-static attributes declared in the C++ snippet returned
395 by an action set's get_attributes(). This is a heuristic: We split the
396 snippet into statements, skip all statements which are static or
397 contain a bracket (functions), and take the last identifier before an
398 optional array size or initialisation.
399
400 """
401 attributes = re.sub(r"/\*.*?\*/", "", attributes, flags=re.S)
402 attributes = re.sub(r"//[^\n]*", "", attributes)
403 result = []
404 for statement in attributes.split(";"):
405 words = statement.split()
406 if not words or "static" in words or "(" in statement:
407 continue
408 match = re.search(r"([A-Za-z_]\w*)\s*(\[[^\]]*\]\s*)*(=.*)?$", statement.strip(), re.S)
409 if match:
410 result.append(match.group(1))
411 return result
412
413
414def _generator_name(submodel) -> str:
415 """!
416
417 Name of the generator class which realises the submodel in C++.
418
419 """
420 generator = getattr(submodel, "generator", None)
421 return "no generator" if generator is None else type(generator).__name__
422
423
424def _is_held_via_smart_pointer(submodel) -> bool:
425 """!
426
427 True if the submodel's generator holds the data via a smart pointer,
428 i.e. if copies of a C++ object share the data rather than cloning it.
429
430 """
431 return "SmartPointer" in _generator_name(submodel)
432
433
434_SMART_POINTER_HINT = (
435 "Realise the data via a smart-pointer generator, e.g. peano4.api.datamodel.PatchToDoubleArrayWithSmartPointer. "
436 "Heap-based generators such as peano4.api.datamodel.PatchToDoubleArrayOnHeap do not suffice, as they deep-copy "
437 "their data whenever an object is copied, and Peano copies the objects when it moves them between its stacks."
438)
439
440
441def _enumerators_used_by(task: Task, task_body: str):
442 """!
443
444 Names of the vertex and face enumerators which a task body might use,
445 i.e. which occur in the generated C++ text. They are realised as
446 peano4::datamanagement::FaceEnumerator or
447 peano4::datamanagement::VertexEnumerator and hold pointers into Peano's
448 stacks, so the spawned task has to capture copies
449 (@ref peano4.api.taskgraph.compiler.SpawnCellTaskActionSet.SpawnCellTaskActionSet).
450
451 @return ([str], [str])
452 Names of the face enumerators and of the vertex enumerators.
453
454 """
455 face_enumerators = [
456 f"{prefix}{submodel.name}"
457 for prefix in ("fineGridFaces", "coarseGridFaces")
458 for submodel in task.face_data
459 if re.search(rf"\b{prefix}{submodel.name}\b", task_body)
460 ]
461 vertex_enumerators = [
462 f"{prefix}{submodel.name}"
463 for prefix in ("fineGridVertices", "coarseGridVertices")
464 for submodel in task.vertex_data
465 if re.search(rf"\b{prefix}{submodel.name}\b", task_body)
466 ]
467 return face_enumerators, vertex_enumerators
468
469
470def _warn_about_unsafe_task(task: Task, action_set_index: int, action_set: "peano4.api.solversteps.ActionSet", task_body: str):
471 """!
472
473 Issue the warnings about task bodies which might not
474 be safe. @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()
475
476 We do not inspect the code. We only search the generated C++ text for
477 the names of data and attributes, i.e. the warnings say that a task
478 might access something.
479
480 """
481 logger = peano4.api.Logger()
482 description = f"action set {action_set_index} ({action_set.get_action_set_name()}) of task {task.name}"
483
484 for accessor in ("fineGridCell", "coarseGridCell"):
485 for submodel in task.cell_data:
486 if f"{accessor}{submodel.name}" in task_body and not _is_held_via_smart_pointer(submodel):
487 logger.warn_and_confirm(
488 f"The touchCellFirstTime() task of {description} might access cell data {submodel.name} ({accessor}{submodel.name}). "
489 f"{submodel.name} is realised via the generator {_generator_name(submodel)}, which does not hold its data via a smart "
490 f"pointer. The task captures the cell data by value, i.e. it works on a copy, and all writes are lost. "
491 + _SMART_POINTER_HINT
492 )
493
494 face_enumerators, vertex_enumerators = _enumerators_used_by(task, task_body)
495 for kind, submodels, enumerators, prefixes, enumerator_type in (
496 ("face", task.face_data, face_enumerators, ("fineGridFaces", "coarseGridFaces"), "peano4::datamanagement::FaceHeapDataEnumerator"),
497 ("vertex", task.vertex_data, vertex_enumerators, ("fineGridVertices", "coarseGridVertices"), "peano4::datamanagement::VertexHeapDataEnumerator"),
498 ):
499 for submodel in submodels:
500 for enumerator in [f"{prefix}{submodel.name}" for prefix in prefixes if f"{prefix}{submodel.name}" in enumerators]:
501 if not _is_held_via_smart_pointer(submodel):
502 logger.warn_and_confirm(
503 f"The touchCellFirstTime() task of {description} might access {kind} data {submodel.name} via {enumerator}. "
504 f"The task therefore captures {enumerator} as {enumerator_type}, which copies the {kind}s. {submodel.name} is "
505 f"realised via the generator {_generator_name(submodel)}, which does not hold its data via a smart pointer, i.e. "
506 f"the copies are deep: The task reads the data as it was when the task has been spawned, and all writes are lost. "
507 + _SMART_POINTER_HINT
508 )
509
510 for attribute_name in _non_static_attribute_names(action_set.get_attributes()):
511 if re.search(rf"\b{attribute_name}\b", task_body):
512 logger.warn_and_confirm(
513 f"The touchCellFirstTime() task of {description} might use the non-static attribute {attribute_name}. The task then "
514 f"accesses an action set object which is destroyed after the producer's mesh traversal."
515 )
516
517
518def _spawn_tasks(split_tasks, task_labels: "peano4.api.datamodel.DaStGen2"):
519 """!
520
521 Make the producers spawn tasks and the consumers wait for them, and add
522 one task number per spawning action set to the task labels.
523 @ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()
524
525 @param split_tasks: [(Task, Task, Task)]
526 Triples of original task, producer and consumer.
527
528 @param task_labels: peano4.api.datamodel.DaStGen2
529 The task labels. This routine adds the attributes.
530
531 @return int
532 Number of attributes added to the task labels.
533
534 """
535 logger = peano4.api.Logger()
536 number_of_task_numbers = 0
537 for task, producer, consumer in split_tasks:
538 user_action_sets = [action_set for action_set in task.action_sets if action_set.user_should_modify_template()]
539 if user_action_sets:
540 logger.warn_and_confirm(
541 f"Task {task.name} holds action sets which the user has to modify ("
542 + ", ".join(action_set.get_action_set_name() for action_set in user_action_sets)
543 + "). The task is split into producer and consumer, but spawns no tasks."
544 )
545 continue
546
547 producer_action_sets = []
548 consumer_action_sets = []
549 previous_task_number_attribute_name = None
550 for action_set_index, action_set in enumerate(task.action_sets):
551 task_body = action_set.get_body_of_operation(peano4.api.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME)
552 if task_body.strip() == "" or not action_set.may_run_in_background_task():
553 # Nothing to spawn, or not worth spawning: The action set
554 # remains a plain action set of the producer and is not part
555 # of the producer-consumer pattern
556 producer_action_sets.append(action_set)
557 continue
558
559 _warn_about_unsafe_task(task, action_set_index, action_set, task_body)
560 task_number_attribute_name = _task_number_attribute_name(task, action_set_index)
561 task_labels.data.add_attribute(dastgen2.attributes.Integer(name=task_number_attribute_name))
562 number_of_task_numbers += 1
563
564 face_enumerators, vertex_enumerators = _enumerators_used_by(task, task_body)
565 producer_action_set = SpawnCellTaskActionSet(
566 action_set,
567 task_labels.name,
568 task_number_attribute_name,
569 previous_task_number_attribute_name,
570 face_enumerators,
571 vertex_enumerators,
572 )
573 producer_action_sets.append(producer_action_set)
574 consumer_action_sets.append(WaitForCellTaskActionSet(producer_action_set))
575 previous_task_number_attribute_name = task_number_attribute_name
576
577 producer.action_sets = producer_action_sets
578 consumer.action_sets = consumer_action_sets
579
580 if previous_task_number_attribute_name is not None:
581 producer.use_cell(task_labels, Task.DataAccess.WRITE)
582 # The consumer only reads the label. Its reset to NoNumber is not
583 # needed downstream, as every producer sets the label in each
584 # sweep. Declaring READ_AND_WRITE would make the labels persist
585 # into the next producer and, through it, into unrelated steps.
586 consumer.use_cell(task_labels, Task.DataAccess.READ_AND_DISCARD)
587
588 return number_of_task_numbers
Wrapper around user-defined attribute.
Singleton logger for the Peano 4 Python API.
Definition Logger.py:37
Default superclass for any data model in Peano which is stored within the grid.
Definition DaStGen2.py:188
Holds a sequence of Task instances.
Definition Graph.py:9
One task within a peano4.api.taskgraph.Graph.
Definition Task.py:8
Producer-side wrapper around an action set, which spawns touchCellFirstTime() as a task.
Consumer-side cousin of a peano4.api.taskgraph.compiler.SpawnCellTaskActionSet.SpawnCellTaskActionSet...
The Python-side model of "what happens during one mesh sweep".
Definition __init__.py:1
str _task_number_attribute_name(Task task, int action_set_index)
CamelCase version of the task's name followed by ActionSet, the action set index and TaskNumber.
List["peano4.api.solversteps.Step"] translate_with_fusion(Graph graph, str identifier, "peano4.api.Project" peano_project)
Split tasks writing cell data into producer and consumer, and fuse tasks into as few mesh traversals ...
List["peano4.api.solversteps.Step"] translate_with_sequential(Graph graph, str identifier, "peano4.api.Project" peano_project)
Split tasks writing cell data into producer and consumer, and map each task onto its own mesh travers...
List[str] _has_events_after_touch_cell_first_time(Task task)
List the events which the task's action sets realise and which a producer would invoke before its spa...
List["peano4.api.solversteps.Step"] _translate(Graph graph, str identifier, "peano4.api.Project" peano_project, bool use_fusion)
Task graph compiler which splits tasks writing cell data into a producer spawning tasks and a consume...
_enumerators_used_by(Task task, str task_body)
Names of the vertex and face enumerators which a task body might use, i.e.
bool _exclusively_writes_cell_data(Task task)
True if task declares at least one cell submodel as WRITE or READ_AND_WRITE, and no face or vertex su...
_warn_about_unsafe_task(Task task, int action_set_index, "peano4.api.solversteps.ActionSet" action_set, str task_body)
Issue the warnings about task bodies which might not be safe.
str _generator_name(submodel)
Name of the generator class which realises the submodel in C++.
Task _copy_task(Task task)
Shallow copy of a task with its own lists and dictionaries, so we can alter the copy's dependencies w...
_split_cell_tasks(Graph graph)
Build the producer-consumer graph.
List[str] _non_static_attribute_names(str attributes)
Names of the non-static attributes declared in the C++ snippet returned by an action set's get_attrib...
bool _is_held_via_smart_pointer(submodel)
True if the submodel's generator holds the data via a smart pointer, i.e.
_spawn_tasks(split_tasks, "peano4.api.datamodel.DaStGen2" task_labels)
Make the producers spawn tasks and the consumers wait for them, and add one task number per spawning ...