3from abc
import abstractmethod
9 Action set (reactions to events)
12 Peano runs through the mesh tied to an observer. The observer
13 "sees" events and issues actions tied to these events. That is
14 the observer listens to the tree traversal for events alike "this
15 is the first time I see (touch) a vertex". A list of events
16 that we can listen to is specified by this class through class
17 attributes starting with OPERATION_. Peano's documentation holds
18 a @ref page_peano_action_sets "discussion of events and their temporal and spatial ordering".
20 For each grid traversal, Peano expects one C++ observer. The
21 observer is represented within Python through
22 peano4.api.solversteps.Step, i.e. this class creates eventually one
23 peano4.api.output.Observer which in return writes out the C++ code.
24 Each observer can have an arbitrary number of action set classes.
25 Each step consequently can hold an action set. The observer's job
26 is it to take information like "I've just entered a spacetree
27 cell" and to break it down into actions like
29 - this vertex is read for the first time
30 - this hanging vertex has to be created
31 - this face is read for the first time
32 - this cell now is entered and these are the associated faces
35 For each of these "actions", the generated C++ code will call the
36 corresponding functions of all associated action sets one by one.
38 @image html ActionSet.png
40 The cartoon above illustrates these three layers: Users or Peano
41 extensions build up logical program steps (left top). They are
42 basically containers with a unique name. Each step holds an arbitrary
43 number of ActionSets. They are, first of all, mappings where each
44 logical action is potentially tied to a C++ code snippet. When
45 the Peano project is generated, each action set is translated into
46 its counterpart in the output namespace which is now a complete
47 description of this class, i.e. holds all functions required, all
48 parameters, ... It copies over the C++ code snippet from its
49 specifiation. This Python representation can then be dumped into
53 ## Lifecycle and parallelisation
55 Action sets are non-persistent, i.e. if you generate C++ code,
56 remind yourself that the class implementing a particular action
57 set will be generated once per grid sweep per tree. As a logical
58 consequence, different action sets are in principle totally
59 independent. If they exchange data, you have to realise this.
60 However, there's a creational routine and a merge command, i.e.
61 you can implement forks and joins (reductions) "natively". There
62 are also functions to inject static data into an action set.
64 There will be one class per action set per context (observer)
65 in which it is used. One object will be created per grid sweep, and
66 it will have the tree identifier -1 by default. The actual
67 objects that then are used are constructed from this prototype
68 object via the augmented copy constructor. The other important
69 role of the prototype object, i.e. the one tied to spacetree id
70 -1, is to manage the grid traversal events. It is this object
71 that is asked to hand out grid traversals. Consult the documentation
72 of get_constructor_body() for details on the tree id.
74 If you want to realise BSP-like programming, then you typically
75 realise the data sharing via the copy constructor. The
76 attributes of the action set then are all thread-local, i.e.
77 there's no need to be afraid of memory races. The join in turn
78 should be done within OPERATION_END_TRAVERSAL. Here, you'll need a
79 semaphore, as the fusion of objects is not per se thread-safe.
82 ## The injected C++ snippets
84 To inject C++ code, i.e. what has to be done, you make get_body_of_operation()
85 return this code if the corresponding argument is passed in. This result
86 should be plain C++ code which the generator really can take and copy
87 n paste into the generated implementation files. If you want these
88 injected snippets to do something solver-specific, a lot of action
89 sets read out there environment and apply jinja2 templates to tailor
90 the snippet to their needs. For example: If you want to do something
91 with the adjacent vertices of a cell, you have to know what these will
92 be called eventually. In this case, it makes sense to study the naming
93 conventions that peano4.api.output.ActionSet will put in place.
95 The easiest way to find these out is to generate some code, to look into
96 the generated output, and then to reconstruct the naming conventions.
97 To be able to do so, it makes sense to let get_action_set_name() return
98 a unique name, so it is easier for you to find what C++ file corresponds
101 A discussion some event functions and their semantics as well as further
102 info on the arguments can be found in the documentation of
103 @ref page_peano_action_sets.
108 The C++ code of each event consists of two parts which you specify
111 - get_guard_of_operation() returns a C++ boolean expression which
112 decides whether the event's code is executed. It defaults to true.
113 - get_body_of_operation() returns the actual C++ code of the event,
114 i.e. the code that is executed if the guard holds. It should not
115 wrap itself into an if statement evaluating the guard.
117 Do not override get_implementation_of_operation(). It combines guard
118 and body into the C++ code that ends up in the generated
119 implementation file, i.e. it yields
125 The routines prepareTraversal(), unprepareTraversal() and
126 createGridControlEventsForNextTraversal() are not tied to a vertex,
127 face or cell and hence have no guard. Their get_implementation_of_...()
128 routines yield the body only.
131 ## Order of invocation and concurrency level
133 The observer invokes the events of a step's action sets in the order
134 of the step's action sets (reversed for the events of leaveCell()).
135 @ref peano4.api.solversteps.Step.Step.add_action_set() sorts the action
136 sets according to their descend_invocation_order.
138 Consecutive action sets with parallel=True form a task group: Per
139 event, the observer spawns their code as one task per action set and
140 waits for all of them, before it continues. An action set with
142 @ref peano4.api.solversteps.SynchroniseActionSets.SynchroniseActionSets
144 (@ref peano4.api.solversteps.SynchroniseActionSets.get_task_groups()).
145 Label an action set as parallel only if its events do not depend on
146 the events of the other action sets of its group and do not write data
153 - @ref page_peano_action_sets
154 - @ref peano4.api.taskgraph
158 @param parallel: Boolean
159 See section on "Order of invocation and concurrency level" in class
162 @param descend_invocation_order: Integer
163 See section on "Order of invocation and concurrency level" in class
169 descend_invocation_order = 0
172 def __init__(self, descend_invocation_order=0, parallel=False):
179 """! Define a tailored constructor body
181 By default, the constructor of an action set is empty. If you you assign
182 attributes to your action set, you however might want to initialise them
183 here. We do not support initialisation lists, to all has to be done via
184 setters unless you create attributes on the heap.
186 The constructor's signature will look similar to
189 EnumerateAndInitSolution2petsc_actionsets_InitVertexDoFs0(int treeNumber);
192 where the treeNumber is -1 if this is the global instance of the
193 action set owned by a rank, or a number greater or equal 0 if this action
194 set is a clone of the glocal action set that's used by one tree traversal.
196 @see @ref peano4.api.solversteps.ActionSet.ActionSet.get_attributes "get_attributes()" to add attributes to your action set
199 return "// @todo Should be overwritten\n"
206 return "// @todo Should be overwritten\n"
211 Return the grid control events that this action set wants to trigger
213 Please consult the documentation of @ref page_peano_action_sets "grid control events" for details on what these events are and how they are used.
216 return "return std::vector< peano4::grid::GridControlEvent >();\n"
221 Return the C++ implementation of createGridControlEventsForNextTraversal()
223 There is no guard for this routine, i.e. the implementation is the
224 body. Do not override this routine, but override
225 get_body_of_createGridControlEventsForNextTraversal().
237 Return the C++ implementation of prepareTraversal()
239 There is no guard for this routine, i.e. the implementation is the
240 body. Do not override this routine, but override
241 get_body_of_prepareTraversal().
253 Return the C++ implementation of unprepareTraversal()
255 There is no guard for this routine, i.e. the implementation is the
256 body. Do not override this routine, but override
257 get_body_of_unprepareTraversal().
262 OPERATION_BEGIN_TRAVERSAL =
"beginTraversal"
263 OPERATION_END_TRAVERSAL =
"endTraversal"
265 OPERATION_CREATE_PERSISTENT_VERTEX =
"createPersistentVertex"
266 OPERATION_DESTROY_PERSISTENT_VERTEX =
"destroyPersistentVertex"
267 OPERATION_CREATE_HANGING_VERTEX =
"createHangingVertex"
268 OPERATION_DESTROY_HANGING_VERTEX =
"destroyHangingVertex"
269 OPERATION_CREATE_PERSISTENT_FACE =
"createPersistentFace"
270 OPERATION_DESTROY_PERSISTENT_FACE =
"destroyPersistentFace"
271 OPERATION_CREATE_HANGING_FACE =
"createHangingFace"
272 OPERATION_DESTROY_HANGING_FACE =
"destroyHangingFace"
273 OPERATION_CREATE_CELL =
"createCell"
274 OPERATION_DESTROY_CELL =
"destroyCell"
276 OPERATION_TOUCH_VERTEX_FIRST_TIME =
"touchVertexFirstTime"
277 OPERATION_TOUCH_VERTEX_LAST_TIME =
"touchVertexLastTime"
278 OPERATION_TOUCH_FACE_FIRST_TIME =
"touchFaceFirstTime"
279 OPERATION_TOUCH_FACE_LAST_TIME =
"touchFaceLastTime"
280 OPERATION_TOUCH_CELL_FIRST_TIME =
"touchCellFirstTime"
281 OPERATION_TOUCH_CELL_LAST_TIME =
"touchCellLastTime"
287 Return actual C++ code snippets to be inserted into C++ code
289 See class' string constants starting with OPERATION_ for possible values
290 of operation_name. The snippet is executed if and only if the guard
291 returned by get_guard_of_operation() holds, i.e. the body should not
292 evaluate the guard itself. See the section "Guard and body" in the
296 return "// @todo Should be overwritten by mapping\n"
301 Return the guard of an operation
303 The guard is a C++ boolean expression. The body of the operation (see
304 get_body_of_operation()) is executed if and only if the guard holds.
305 The guard can use the same arguments as the body, i.e. the marker and
306 the fine and coarse grid data. By default, the guard is true, i.e. the
307 body is always executed.
309 See class' string constants starting with OPERATION_ for possible values
318 Return the C++ implementation of an operation
320 Combines the guard (see get_guard_of_operation()) and the body (see
321 get_body_of_operation()) into
327 If the body is empty or the guard is true, the routine yields the
328 body only. Do not override this routine, but override
329 get_guard_of_operation() and get_body_of_operation().
334 if body.strip() ==
"" or guard.strip() ==
"true":
336 return " if (" + guard +
") {\n" + body +
"\n }\n"
342 Return unique action set name
344 Returns a description (word) for the mapping which is also used as class name
345 for the generated type. As a consequence, the result should be one word (if
346 possible) and uppercase. Also, every subclass should overwrite this routine.
348 The generator will take the result and construct eventually classes similar to
349 MyStep2Dummy.h and MyStep2Dummy.cpp or similar for the example below, where we
359 Is the user allowed to modify the output
361 Return whether you expect the user to modify the generated code. If this
362 is the case, then the API places the generated output in the directory
363 actions. Otherwise, it goes into the observer directory and will be
364 overwritten in each and every Python run.
372 May a task graph compiler move touchCellFirstTime() into a task
374 Some task graph compilers spawn the touchCellFirstTime() event of an
376 (@ref peano4.api.taskgraph.compiler.CellTaskProducerConsumer._translate()).
377 Return False if this is not worth it, e.g. as the event is so cheap
378 that the tasking overhead dominates. The action set is then invoked
379 directly within the mesh traversal. By default, every action set may
389 Return attributes as copied and pasted into the generated class.
391 Please note that action sets are not persistent, i.e. there is one
392 object creation per grid sweep per tree.
401 Return include statements that you need.
403 All of these includes will eventually end up in the header of the generated
Action set (reactions to events)
__init__(self, descend_invocation_order=0, parallel=False)
get_body_of_unprepareTraversal(self)
get_body_of_createGridControlEventsForNextTraversal(self)
Return the grid control events that this action set wants to trigger.
bool may_run_in_background_task(self)
May a task graph compiler move touchCellFirstTime() into a task.
get_destructor_body(self)
int descend_invocation_order
get_static_initialisations(self, full_qualified_classname)
get_action_set_name(self)
Return unique action set name.
get_constructor_body(self)
Define a tailored constructor body.
get_implementation_of_operation(self, operation_name)
Return the C++ implementation of an operation.
get_body_of_prepareTraversal(self)
get_implementation_of_createGridControlEventsForNextTraversal(self)
Return the C++ implementation of createGridControlEventsForNextTraversal()
get_includes(self)
Return include statements that you need.
user_should_modify_template(self)
Is the user allowed to modify the output.
get_guard_of_operation(self, operation_name)
Return the guard of an operation.
get_implementation_of_prepareTraversal(self)
Return the C++ implementation of prepareTraversal()
get_body_of_operation(self, operation_name)
Return actual C++ code snippets to be inserted into C++ code.
get_implementation_of_unprepareTraversal(self)
Return the C++ implementation of unprepareTraversal()
get_attributes(self)
Return attributes as copied and pasted into the generated class.