-
Notifications
You must be signed in to change notification settings - Fork 5
Meshes
The objective of this tutorial is to illustrate the general use of the Mesh
data structure.
To get an overview of the data structure the reference Mesh is a good way to start.
It is also recommended to take a look at the VCycle operator since it is also used in this tutorial.
This tutorial only shows the important parts of an actual implementation. If you look for the full implementation that uses the Mesh
data structure you can look at the Mesh simulation sources directory.
For all following code examples we will use the namespace:
using namespace allscale::api::user;
This tutorial covers the following steps:
-
MeshBuilder - building our
Mesh
-
MeshProperties - how to create and use
MeshProperties
-
Mesh - how to use the
Mesh
-
VCycle - simulate using the
VCycle
We first define the types of the nodes in the mesh.
When building the mesh we create nodes of these types and link them.
MeshProperties
allow to associate a value to each node of a certain kind on each level of a mesh.
// nodes of our mesh
struct Cell {};
struct Face {};
struct Node {};
struct LeftBoundaryFace {};
struct RightBoundaryFace {};
Next we define edges to connect nodes on the same level. These edges inherit from allscale::api::user::data::edge<NodeKindA,NodeKindB>
. The NodeKind
s are of the node types we defined before.
For the simulation we define the following edges:
// edges
struct Face_2_Cell_Left : public data::edge<Face,Cell> {};
struct Face_2_Cell_Right : public data::edge<Face,Cell> {};
struct BoundaryFace_2_Cell_Left : public data::edge<RightBoundaryFace,Cell> {};
struct BoundaryFace_2_Cell_Right : public data::edge<LeftBoundaryFace,Cell> {};
struct Cell_2_Face_Left : public data::edge<Cell,Face> {};
struct Cell_2_Face_Right : public data::edge<Cell,Face> {};
struct Node_2_Cell : public data::edge<Node,Cell> {};
struct Face_2_Node : public data::edge<Face,Node> {};
In the example code above the Face_2_Cell_Left
edge connects a Face
with a Cell
.
The hierarchies inherit from allscale::api::user::data::hierarchy<NodeKindA,NodeKindB>
. They are similar to the edges but connect nodes on adjacent levels instead of the same level.
// hierarchies
struct Cell_2_Child : public data::hierarchy<Cell,Cell> {};
struct Face_2_Child : public data::hierarchy<Face,Face> {};
struct LeftBoundaryFace_2_Child : public data::hierarchy<LeftBoundaryFace,LeftBoundaryFace> {};
struct RightBoundaryFace_2_Child : public data::hierarchy<RightBoundaryFace,RightBoundaryFace> {};
The hierarchy Cell_2_Child
connects a Cell
with a Cell
on adjacent levels.
The MeshBuilder
takes the nodes, edges and hierarchies and number of levels as template arguments:
template<unsigned levels = 1>
using MeshBuilder = data::MeshBuilder<
data::nodes<
Cell,
Face,
Node,
LeftBoundaryFace,
RightBoundaryFace
>,
data::edges<
Face_2_Cell_Left,
Face_2_Cell_Right,
BoundaryFace_2_Cell_Left,
BoundaryFace_2_Cell_Right,
Node_2_Cell,
Face_2_Node,
Cell_2_Face_Left,
Cell_2_Face_Right
>,
data::hierarchies<
Cell_2_Child,
LeftBoundaryFace_2_Child,
RightBoundaryFace_2_Child
>,
levels
>;
The MeshBuilder
provides the utility to build a Mesh
data structure.
The code below creates a new node of type Cell
and a new node of type Face
on level 0
. Both calls return a NodeRef
to the newly created node.
auto cellRef = builder.template create<Cell, 0>();
auto faceRef = builder.template create<Face, 0>();
The created nodes can be connected using the link
method.
Given the edge kind as template argument and the node references as method arguments the two nodes are linked using the link
method.
builder.link<Face_2_Cell_Left>(faceRef, cellRef);
The above code creates an edge between the nodes faceRef
and cellRef
.
Note that the method arguments have to be in the right order.
The link
method can also be used to link nodes on adjacent levels (i.e. hierarchies).
In the tutorial sources we build a tube with a given number of cells. Going up one level in the hierarchy will halve the number of cells compared to the lower level.
If you are interested in the implementation details take a look at the TubeLayerBuilder
class in the Mesh simulation sources directory.
If all needed nodes are created and linked the Mesh
data structure can be created:
Mesh mesh = std::move(builder).build();
If the MeshBuilder
isn't used further it is desireable to call the build
method on a rvalue reference.
The mesh properties help to associate values to a certain kind of node on each level of the mesh. For the simulation we define the following mesh properties:
using value_t = double;
struct CellVolume : public data::mesh_property<Cell,value_t> {};
struct CellCenter : public data::mesh_property<Cell,Point> {};
struct FaceSurface : public data::mesh_property<Face,value_t> {};
struct LeftBoundaryFaceSurface : public data::mesh_property<LeftBoundaryFace,value_t> {};
struct LeftBoundaryFaceCenter : public data::mesh_property<LeftBoundaryFace,Point> {};
struct RightBoundaryFaceSurface : public data::mesh_property<RightBoundaryFace,value_t> {};
struct RightBoundaryFaceCenter : public data::mesh_property<RightBoundaryFace,Point> {};
struct NodePosition : public data::mesh_property<Node,Point> {};
The above code creates eight different properties. Each property associates a value to every node of a certain kind. e.g. the property CellVolume
associates a value_t
(double
) to each Cell
on each level.
To receive the MeshProperties
object we call the createProperties
method of the Mesh
data structure:
auto properties = mesh.createProperties<CellVolume,
CellCenter,
FaceSurface,
LeftBoundaryFaceSurface,
LeftBoundaryFaceCenter,
RightBoundaryFaceSurface,
RightBoundaryFaceCenter,
NodePosition>();
After creating the MeshProperties
data structure the values of each property can be accessed.
The get
method of the MeshProperties
data structure provides access to the values on a given layer:
auto& data = properties.get<RightBoundaryFaceSurface,0>();
data[noderef]; // access the value for noderef
The above code returns MeshData
which provides access by NodeRef
using the subscript operator to get the specific value for a node.
There is also a way to directly access a value of a node by passing the NodeRef
as argument to the get
method:
properties.get<RightBoundaryFaceSurface,0>(noderef);
The Mesh
is the data structure that holds the topological information of a hierarchical mesh.
It provides methods to iterate over given node types and levels in sequential or in parallel order.
The code below sets the property CellVolume
to 1
:
auto& cellVolume = properties.template get<CellVolume>();
mesh.forAll<Cell>([&](auto& c) {
// do something with cell c
// e.g. update a property value of c
cellVolume[c] = 1;
});
Note that the above code only updates the value of all nodes of kind Cell
on level 0
because the forAll
method takes a second template argument to select the level which is implicitly 0
if no argument is given.
The method pforAll
is the parallel version of forAll
.
Further there are methods to access neighbor nodes depending on the structure we defined in the MeshBuilder
by linking nodes.
// get a NodeList of all sinks for faceRef and edge "Face_2_Cell_Left"
auto nodes = mesh.getSinks<Face_2_Cell_Left>(faceRef);
// get the NodeRef of the only sink for faceRef and edge "Face_2_Cell_Left"
auto node = mesh.getSink<Face_2_Cell_Left>(faceRef);
// get a NodeList of all sources for cellRef and edge "Face_2_Cell_Left"
auto nodes = mesh.getSources<Face_2_Cell_Left>(cellRef);
// get the NodeRef of the only source for cellRef and edge "Face_2_Cell_Left"
auto node = mesh.getSource<Face_2_Cell_Left>(cellRef);
// neighbor methods return cell nodes because a face node is given as the argument
// in general these methods return the result of
// getSink(s) or getSource(s) depending on the method argument type
auto nodes = mesh.getNeighbors<Face_2_Cell_Left>(faceRef);
auto node = mesh.getNeighbor<Face_2_Cell_Left>(faceRef);
Just like for linked nodes on the same level there are also access methods for adjacent levels.
// returns a NodeList of children of c for hierarchy "Cell_2_Child"
auto children = mesh.template getChildren<Cell_2_Child>(c);
// returns the parent NodeRef of c for hierarchy "Cell_2_Child"
auto parent = mesh.template getParent<Cell_2_Child>(c);
In addition to MeshProperties
the method createNodeData()
creates values for each node on a single level.
auto& data = mesh.createNodeData<Cell,value_t,0>();
data[c]; //access data for NodeRef c
The method returns MeshData
which can be accessed with the subscript operator with a NodeRef
as the argument.
The above code creates a double for each Cell
on level 0
.
As for the previous methods the level can be omitted if it is 0
.
A single iteration of the V-cycle computes new values on each hierarchical level starting at the bottom, going to the top and back down afterwards.
// -- VCycle stage --
template<typename Mesh, unsigned Level>
struct TemperatureStage {
void computeFineToCoarse() {
// compute stage while going up the hierarchy
// ...
}
void computeCoarseToFine() {
// compute stage while going down the hierarchy
// ...
}
void restrictFrom(TemperatureStage<Mesh,Level-1>& childStage) {
// go from child stage to this stage
// ...
}
void prolongateTo(TemperatureStage<Mesh,Level-1>& childStage) {
// go from this stage to child stage
// ...
}
}
One iteration of the V-cycle starts at the lowest level of the mesh hierarchy and calls the computeFineToCoarse
method, goes up one level and calls the restrictFrom
method. These calls are repeated until the top of the hierarchy is reached.
If the top level of the hierarchy is reached the VCycle goes down the hierarchy by calling prolongateTo
followed by computeCoarseToFine
repeatedly until the bottom level is reached.
When the bottom level is reached one iteration of the V-cycle was done.
For a detailed implementation of the TemperatureStage
you should again take a look at the Mesh simulation sources directory.
using vcycle_type = algorithm::VCycle<
TemperatureStage,
decltype(mesh)
>;
vcycle_type vcycle(mesh);
// run 10 iterations
vcycle.run(10);
The above code runs ten iterations of our TemperatureStage
V-cycle on our mesh
.
Part of the AllScale project - http://www.allscale.eu