![]() |
Peano
|
Peano has a variety of functions which are called during spacetree traversal events.
For a full description, please see the original article on Peano. These functions are used to inject domain behaviour into the mesh traversals:
peano4.solversteps.ActionSet
Every event is mapped onto actions, aka function calls. Users can associate multiple action sets with each travesal, such that the automaton calls a series of actions from the individual action sets for each event.
Within the Python API, peano4.solversteps.ActionSet represents an action set. This class represents the abstraction layer that we use to model the actual event behaviour.
Event | Semantics | Dependencies |
---|---|---|
prepareTraversal | Static routine of an action set which is called before any action set for the particular mesh travesal is created at all. | Precedes the construction of the action sets for the next mesh traversal. Called once per MPI rank only. Study this routine in the context of data decomposition discussion. |
unprepareTraversal | Inform listeners about end of traversal on this particular subtree after all action sets have termianted and have been destructed. Static binding. | Very last event per rank. Study this routine in the context of data decomposition discussion. |
----------— | ----------— | ----------— |
beginTraversal | Inform listeners about start of traversal on this particular subtree. | Called prior to any other event in this grid sweep. |
endTraversal | Inform listeners about end of traversal on this particular subtree. | Called after all other event in this grid sweep. |
----------— | ----------— | ----------— |
createPersistentVertex | Invoked in the context of dynamic mesh refinement when we refine. Usually used to initialise data. | Called before anything is done with this vertex and notably prior to touchVertexFirstTime. |
destroyPersistentVertex | Invoked in the context of dynamic mesh refinement when we erase. Might be used to wrap up data or project data that otherwise would be lost onto coarser resolution levels. | Called after touchVertexLastTime. No further event will be triggered for this vertex. All adjacent faces have been destroed before. |
createPersistentFace | Invoked in the context of dynamic mesh refinement when we refine. Usually used to initialise data. | Called before anything is done with this face and notably prior to touchFaceFirstTime. All adjacent vertices have been created before. |
destroyPersistentFace | Invoked in the context of dynamic mesh refinement when we erase. Might be used to wrap up data or project data that otherwise would be lost onto coarser resolution levels. | Called after touchVertexLastTime. No further event will be triggered for this face. |
createCell | Invoked in the context of dynamic mesh refinement when we refine. Usually used to initialise data. | Called before anything is done with this cell and notably prior to touchCellFirstTime. All adjacent faces and vertices have been created before. |
destroyCell | Invoked in the context of dynamic mesh refinement when we erase. Might be used to wrap up data or project data that otherwise would be lost onto coarser resolution levels. | Called after touchCellLastTime. No further event will be triggered for this cell. Precedes the destruction of the adjacent faces and vertices. |
----------— | ----------— | ----------— |
createHangingVertex | Invoked in the context of adaptive meshes. Hanging vertices are not persistent aka stored in-between two mesh traversals. Actually, the traversal automaton might create them multiple times, i.e. up to \( 2^d-1 \) times throughout the traversal if it wants. Used to project data from the next coarser level onto the hanging entity. | Called before any event for an adjacent face or cell is invoked. |
destroyHangingVertex | Invoked in the context of adaptive meshes. Hanging vertices are not persistent aka stored in-between two mesh traversals and hence are destroyed for every creation encountered per mesh sweep. | Last action happening for a vertex. As we traverse the tree top-down, all the coarser parent data are still valid and no touchLastXXX event has been called for those guys. |
createHangingFace | Invoked in the context of adaptive meshes. Hanging faces are not persistent aka stored in-between two mesh traversals. Used to project data from the next coarser level onto the hanging entity. | Called before any event for an adjacent cell is invoked. |
destroyHangingFace | Invoked in the context of adaptive meshes. Hanging faces are not persistent aka stored in-between two mesh traversals and hence are destroyed for every creation encountered per mesh sweep. | Precedes the destruction of adjacent hanging vertices. |
----------— | ----------— | ----------— |
touchVertexFirstTime | Invoked when vertex is loaded for the first time or directly after its creation. | Pior to any other operation on this vertex or an adjacent face or cell. Called directly after createPersistentVertex if this is a brand new vertex. All first and creational events on all coarser data have been called before. |
touchVertexLastTime | Invoked when vertex is accessed for the last time in this mesh sweep. | After all adjacent cells have been traversed and all "last" operations on all adjacent faces and cells have complted. destroyPersistentVertex is the only event that might be called afterwards for this vertex in this sweep if the mesh erases. All first and creational events on all coarser data have been called before. |
touchFaceFirstTime | Invoked when face is loaded for the first time. | Invoked after touchVertexFirstTime for adjacent vertices. Precedes event on the two adjacent cells. All first and creational events on all coarser data have been called before. |
touchFaceLastTime | Invoked when face is accessed for the last time in this mesh sweep. | After all adjacent cells have been traversed (see touchCellLastTime). All first and creational events on all coarser data have been called before. |
touchCellFirstTime | Invoked when we hit the cell. | After all adjacent hanging faces and vertices have been created. You can assume that touchXXXFirstTime for the persistent adjacent grid entities has been called. Also, all first events on all coarser data have been called before. |
touchCellLastTime | Invoked when the traversal automaton moves from fine to coarse cells within the spacetree. | Called once all events on finer levels on children within the spacetree have been called. |
----------— | ----------— | ----------— |
We illustrate some of this behaviour by means of a sketch we usually use to explain domain decomposition. So please ignore the colours of the cells in this example:
In a serial code, Peano runs through the mesh. Here's some statements on events triggered throughout the traversal:
Whenever you encounter a face, vertex or cell, Peano's action sets will be given a single or set of the grid entities. That is, for each vertex, face and cell data set, the corresponding events will get a reference or pointer to them. So now you can manipulate them.
Cells, faces and vertices do not carry information such as position. They also do not know if they are local, they are hanging, ... Such information is held separatedly. We actually do not hold it at all, but compute it on-the-fly within the Peano core. To the action set, the information is exposed through a marker object. That is, every action set is also passed a marker object which you can query to get this information.
Please note that all grid entities seem to hold positions and mesh sizes. However, these properties are only available in debug mode and should not be used by user code. I use them to print out meaningful debug data (so you know where the code is within the mesh) and I use them within assertions to check if the grid is consistent, i.e. Peano's core stores the right mesh data at the right place.
The markers of relevance are
The actions reacting to events, i.e. operations within the action set, also have access to the direct parent data one level coarser. Peano always only passes through pointers to coarser data. For each vertex data, you will for example get one pointer to the corresponding data. To access the right data, Peano passes in an enumerator object. You have to use the enumerator to pick the correct element from the array identified by the pointer.
Enumerators of interest are:
There's no cell enumerator, as you never get more than one cell.
An algorithmic step (Observer) within Python corresonds 1:1 to a class of the type peano4::grid::TraversalObserver, and it holds a set of actions. Actions are represented by the Python class peano4.solversteps.ActionSet. Each action set within an observer (algorithmic step) has two relevant properties:
These flags (set in the constructor) give the user control over the order of the actions for each even throughout the traversal. They also allow you to specify if events can run in parallel. Again: This is the control how two touchCellFirstTime() events from two different action sets are triggered for the same cell on one tree simultaneously. It has nothing to do with data decomposition, i.e. how the trees are split.