GridFormat 0.2.1
I/O-Library for grid-like data structures
|
GridFormat
uses a traits (or meta-function) mechanism to operate on user-given grid types. As a motivating example, consider this piece of code:
do_something_on_a_grid
iterates over all cells of the grid, and you can imagine that it then goes on to compute some stuff. The point is that with this way of writing the function, it is only compatible with grids that have a public cells()
function which returns something over which we can iterate. This function would thus be incompatible with user-defined grids that provide a different way of iterating over the cells. One possibility would be to require users to write an adapter, but then they possibly also have to adapt the cell type that the iterator yields, etc...
Instead, in GridFormat
, any piece of code that operates on user grids is written in terms of traits, and the code above would turn into something like:
The code now expects that there exist a specialization of the Cells
trait for the given grid. This allows users to specialize that trait for their grid data structure, thereby making it compatible with GridFormat
. See the code below for an idea about how the traits specialization may look. For more details on this, please have a look at the resources referenced in the main readme.
As discussed in the overview over supported kinds of grids, GridFormat
understands the notion of unstructured, structured, rectilinear or image grids. The reason for this is that some file formats are designed for specific kinds of grids and can store the information on their topology in a space-efficient manner. To see which format assumes which kind of grid, see the API documentation. In the code, there exist concepts for each of these kinds of grids, which essentially check if the required traits are correctly implemented. When implementing the traits for your grid type, it is helpful to use these concepts in order to verify your traits implementations. For instance, you may use static_asserts
:
The remainder of this page discusses the different traits used by GridFormat
, and which ones need to be specialized in order to model a particular kind of grid. Note that all traits presented in the following are declared in the namespace GridFormat::Traits
. In case you want to use GridFormat
in parallel computations, please make sure to also read the related section at the end of this page.
template<typename Grid> struct Cells;
This trait exposes how one can iterate over the cells of a grid. Specializations must provide a static function get(const Grid&)
that returns a forward range of cells. GridFormat
deduces the cell type used by the Grid
from this range. As an example, let's consider a grid implementation (SomeUnstructuredGrid
) that identifies cells solely by an index. In this case, the following would be a valid specialization, which yields int
as cell type:
template<typename Grid> struct Points;
This trait exposes how one can iterate over the points of a grid. Specializations must provide a static function get(const Grid&)
that returns a forward range of points. GridFormat
deduces the point type used by the Grid
from this range. As an example, let's again consider a grid implementation (SomeUnstructuredGrid
) that identifies points solely by an index. As before, we can simply return an index range yielding int
as point type (GridFormat
supports the case of cell & point types being the same):
In the following we will discuss the traits required for particular grid concepts. Some of these traits expose information on a single cell or point of the grid, and therefore, have to be specialized for the grid and its point or cell type. As discussed above, GridFormat
deduces these types from the Cells
and Points
traits. In the following, we will refer to these types as Cell
and Point
.
template<typename Grid, typename Cell> struct CellPoints;
This trait exposes how one can iterate over the points of an individual grid cell. Specializations must provide a static function get(const Grid&, const Cell&)
that returns a range of points that are contained within the given cell. Note that the value_type of the returned range must be convertible to the point type deduced from the Points
trait. Moreover, point ordering is expected to follow the conventions used by VTK. Following the above example, an implementation of this trait could look like this:
template<typename Grid, typename Cell> struct CellType;
This trait exposes the geometry type of an individual grid cell. Specializations must provide a static function get(const Grid&, const Cell&)
that returns an instance of the CellType enum. For the above example, the specialization of this traits could look like this:
template<typename Grid, typename Point> struct PointCoordinates;
This trait exposes the coordinates of a grid point. Specializations must provide a static function get(const Grid&, const Point&)
that returns the coordinates of the given point as a statically sized range. From the size of this range, GridFormat
deduces the space dimension of the grid at compile-time. If your grid does not know the space dimension at compile-time, you can simply return an std::array<double, 3>
with zero padding. For the above example, a specialization of this trait may look as follows:
template<typename Grid, typename Point> struct PointId;
This trait exposes a unique id for individual points of a grid. Specializations must provide a static function get(const Grid&, const Point&)
that returns a unique id (as integer value, e.g. std::size_t
) for the given point. For our example above, we could directly return the Point
, since we chose the Points
trait to simply return a range over all point indices:
In addition to the traits below, the StructuredGrid
concept also requires that the PointCoordinates
trait is implemented (see above).
template<typename Grid> struct Extents;
This trait exposes the number of cells of the structured grid in each coordinate direction. Specializations must provide a static function get(const Grid&)
that returns a statically sized range, whose size is equal to the dimension of the grid. As an example, let's consider a structured grid implementation SomeStructuredGrid
that has a num_cells(int direction)
function:
template<typename Grid, typename Entity> struct Location;
This trait exposes the index tuple of a given entity within the structured grid. For a visualization and the assumptions on the orientation, see the overview over supported kinds of grids. This trait must be specialized for both Cell
and Point
(i.e. for two types of Entity
), and they must provide a static function get(const Grid&, const Entity&)
that returns a statically sized range whose elements are integer values (the indices of the entity) and whose size is equal to the dimension of the grid. As an example, let's consider a grid implementation that has points and cells that carry information about their location within the grid. An implementation of this trait could then look like this:
In addition to the Ordinates
trait below, the RectilinearGrid
concept also requires that the Extents
and Location
traits are implemented (see above).
template<typename Grid> struct Ordinates;
This trait exposes the ordinates of a RectilinearGrid
grid along the coordinate axes. Specializations must provide a static function get(const Grid&, unsigned int direction)
that returns a range over the ordinates along the axis specified by direction
(direction < dim
, where dim is the dimension of the grid). The size of the range must be equal to the number of cells + 1 in the given direction. As an example, let's consider an implementation SomeRectilinearGrid
with the following specialization for this trait:
In addition to the traits below, the ImageGrid
concept also requires that the Extents
and Location
traits are implemented (see above).
template<typename Grid> struct Origin;
This trait exposes the position of the lower-left corner of the grid, that is, the position of the point at index \((0, 0, 0)\) (for a visualization see here Specializations must provide a static function get(const Grid&)
that returns a statically sized range, whose size is equal to the dimension of the grid. An exemplary specialization of this trait could look like:
template<typename Grid> struct Spacing;
This trait exposes the spacing between grid points along the axes (in other words, the size of the cells in each coordinate direction). Specializations must provide a static function get(const Grid&)
that returns a statically sized range, whose size is equal to the dimension of the grid. As an example, a valid specialization of this trait may be:
template<typename Grid> struct NumberOfPoints;
This trait exposes the number of points of a grid, and if not specialized, the number of points is deduced from the size of the range obtained from Points
. If your point range is not a sized range, however, specializing this trait can lead to an improved efficiency. Specializations must provide a static function get(const Grid&)
that returns the number of points as an integral value.
template<typename Grid> struct NumberOfCells;
This trait exposes the number of cells of a grid, and if not specialized, the number of cells is deduced from the size of the range obtained from Cells
. If your cell range is not a sized range, however, specializing this trait can lead to an improved efficiency. Specializations must provide a static function get(const Grid&)
that returns the number of cells as an integral value.
template<typename Grid, typename Cell> struct NumberOfCellPoints;
This trait exposes the number of points in a grid cell, and if not specialized, the number of points is deduced from the size of the range obtained from CellsPoints
. If that range is not a sized range, however, specializing this trait can lead to an improved efficiency. Specializations must provide a static function get(const Grid&, const Cell&)
that returns the number of points in the cell as an integral value.
template<typename Grid> struct Basis;
This trait can be specified for image grids in order to specify their orientation. Per default, an image grid is assumed to be axis-aligned, that is, the default basis (in 3D) is
Specializing the Basis
trait, you can implement the static function get(const Grid&)
and return a custom basis. It is expected that the return type from this function is a two-dimensional, statically-sized range, with the outer and inner dimensions being equal to the grid dimension.
template<typename T> struct StaticSize;
GridFormat
requires that the types returned from some traits model the StaticallySizedRange
concept. Statically sized means that the size of the range is known at compile time. Per default, GridFormat
accepts std::array
, std::span
(with non-dynamic extent), T[N]
or anything that either has a constexpr static std::size_t size()
function or a member variable named size
that can be evaluated at compile time. If your type does not fulfill any of these requirements, but is in fact range with a size known at compile-time, you can also specialize the StaticSize
trait for the type you want to return. Alternatively, you can of course just convert your type into an std::array
within the trait that expects you to return a statically sized range. To give an example, let's consider a Vector
class whose size is known at compile-time, but does not fulfill the requirements for GridFormat
to automatically identify it as a StaticallySizedRange
:
In order to use GridFormat
to write grid files from parallel computations using MPI, make sure that you implement the above traits such that they only return information on one partition. Moreover, GridFormat
currently does not provide any means to flag cells/points as ghost or overlap entities, and therefore, it is expected that you only provide information on the collection of interior entities, that is, the partitions are expected to be disjoint. To be more concrete, it is expected that
Cells
trait provides a range over the interior cells of a partition, only.Points
trait provides a range over only those points that are connected to interior cells (otherwise there will be unconnected points in the output, however, it should still work).Extents
trait provides the number of cells of the partition, not counting any ghosts or overlap cells.Ordinates
trait provides the ordinates of the partition, only including points that are connected to interior cells.Origin
trait returns the lower-left corner of the partition without ghost cells.If you include ghost entities in your traits, output should still work, but there will be overlapping cells.