Lightweight Periodic Dataset Generation

In this blog article, we present the recently introduced VTK filter called vtkAngularPeriodicFilter ("Angular Periodic Filter" in ParaView's 4.4 filter list), and its underlying memory efficient data array vtkPeriodicDataArray.

The objective of the project was to provide an efficient way to describe periodic datasets – ie. datasets that can be generated by applying a transformation on a part of the model to obtain the other ones, for example the blades of a propeller.

A naive approach would be to transform (with vtkTransform filter for instance) an initial part for each period to obtain the full mesh (a multiblock or multipiece dataset). This approach is OK if you deal with reasonnably small datasets and if the number of periods is small, but otherwise it can quickly blow the memory.

The alternative to overcome this memory consumption problem is to perform the data transformation on the fly, that is in a procedural way – of course at the price of more computation ressources, but you cannot have the cake and eat it too! 🙂

Angular Periodic Filter

This filter enables the generation of periodic meshes of selected blocks of a multiblock dataset (use the Group filter to generate a multiblock from a single dataset object).

A typical example is to create the mesh from a part of a periodic rotative piece.

With the AngularPeriodicFilter it is very easy and almost memory free to generate the whole piece.

The same results could be archived using iterations of Transform and Append filters, but the memory usage would be quite bigger.

Note that the filter generates a multiblock dataset with the same hierarchy than the original ones: dataset blocks that were selected for transformation are replaced by multipiece datasets (the first piece is the original one, period #0, the other ones are the other periods), the blocks that were not selected are simply shallow-copied.

The new filter parameters are the Axis of Rotation (X, Y or Z), the center of rotation, the angle of rotation (this parameter can be provided manually or fetch from a field data value) and finally the number of periods to generate.

Implementation Details: Periodic Data Arrays

The base for this filter is a new type of array we called vtkPeriodicDataArray class which is a subclass of vtkMappedDataArray – please refer to Berk Geveci's series of blog articles about Zero Copies Array, see here and there. A vtkPeriodicDataArray basically computes the requested array values according to the periodic transformation parameters and an original array each time a value is requested from the array: each time the value of such an array is accessed, instead of simply returning the value saved in memory, it reads the value in the original array and transforms it – of course, there is some simple caching optimization, but it can still be quite demanding.

vtkPeriodicDataArray is an abstract class for different kinds of periodic data transform policies. At the moment, the only available subclass is vtkAngularPeriodicDataArray. This class which enables periodic transformation according to the rotation of a given angle around an axis with an optional translation. It works with 3 components vectors (point coordinates included) and tensors.

It is not as generalistic as a Transform filter, as there were no specific needs for all types of rotations, and this simplification enables mathematical optimizations. The design facilitates the implementation of other kinds of periodic arrays to be implemented easily. This design works very well memory wise, but there is one big drawback with it:

each time GetVoidPointer is called, the entire array is computed and saved in memory.

Hopefully GetVoidPointer is not the standard way to access data anymore, and the use of std iterator std::copy enables access to data with a little overhead compared to the GetVoidPointer version.

For example this copy data using GetVoidPointer code:

void copy( vtkDataArray* in, vtkDataArray* out)
{
double* ptr = in->GetVoidPointer;
double* ptr2 = out->GetVoidPointer;
  for (int i = 0; i < in->GetNumberOfElements(); i++)
  {
      ptr2[i] = ptr[i];
  }

}

Can be written this way :

void copy(vtkDataArray *in, vtkDataArray *out)
{
  switch (src->GetDataType())
    {  
    vtkDataArrayIteratorMacro(src,
      copy(dest, src, vtkDABegin, vtkDAEnd));
    }  
}

template <class InputIterator>
void copy(vtkDataArray *dest, vtkDataArray *src,
                                   InputIterator srcIt,  InputIterator srcEnd)
{
  switch (dest->GetDataType())
    {  
    vtkDataArrayIteratorMacro(dest,
      copy(dest, src, srcIt, srcEnd, vtkDABegin));
    }  
}

template <class InputIterator, class OutputIterator>
void copy(vtkDataArray *dest, vtkDataArray *src,
          InputIterator srcIt,
          InputIterator srcEnd, OutputIterator destIt)
{
   // Copy data, performing implicit conversion if necessary
  std::copy(srcIt, srcEnd, destIt);
}

This allows efficient iteration through mapped or non-mapped data arrays.

Note that there are still some filters using GetPointer though (they will generate a warning message), so these are to be avoided when working with PeriodicDataArray.

The vtkAngularPeriodicFilter makes good use of the new array type, generating transformed vtkPoints, vtkPointData and vtkCellData for a minimal memory usage. Along the way, a new simpler version of vtkPoints::GetBounds() was implemented, in order to avoid a computation of all AngularPeriodicDataArray values, since the range of a AngularPeriodicDataArray can be computed through a transformation of the bounds of the original data array.

Questions or comments are always welcome!