Retrieving From Values in Topology Maps

Jump to navigation Jump to search

When executing a topology map operation, the worklet can (and almost always does) receive field information on the from topology, which has a many-to-one mapping on the worklet invocation. Basically that means that there is a group of values passed into the worklet.

Typically, a group of values of the same type are stored in a vtkm::Vec class, and the current implementation basically uses that. However, the size of this Vec is not known at compile time so as a kludge you have to specify some maximum size somewhere. This is problematic for numerous reasons.

I propose changing the code such that we do not have to specify an actual or max value at runtime. Rather, we build classes that can provide a dynamic list at runtime, but without actually dynamically allocating memory in the execution environment (which is a bad idea).

Vec-like Objects

There are a number of combinations in which from values are passed to a topology map worklet. The values could come from explicit or structured grids. The values could be indices, fields, or point coordinates. Each combination could be handled differently. Rather than force all these combinations pass data in the same class type (such as vtkm::Vec or vtkm::exec::TopologyData, I propose we allow passing the worklet an unknown type. The type will be a Vec-like object that behaves much like a Vec object except that the number of components is not known at compile time.

The implementation for the CellAverage worklet can look something like this:

class CellAverage : public vtkm::worklet::WorkletMapTopology
  typedef void ControlSignature(TopologyIn topology,
                                FieldInFrom<Scalar> inField,
                                FieldOut<Scalar> outField);
  typedef _3 ExecutionSignature(_2);

  template<typename InFieldType>
  typename InFieldType::ComponentType
  operator()(const InFieldType &inField) const
    vtkm::IdComponent numComponents = inField.GetNumberOfComponents();
    typename InFieldType::ComponentType sum = inField[0];
    for (vtkm::IdComponent index = 1; index < numComponents index++)
      sum += inField[index];

    return sum/numComponents;

Contract for Vec-like Objects

Being that the worklet coder no longer knows the exact type for the Vec or Vec-like object being passed, we have to establish a set of rules, a "contract," of behavior that can be expected.

Informally, a Vec-like object should offer everything a Vec offers with the exception of the NUM_COMPONENTS static const variable, which cannot be known. These Vec-like objects also do not need to support math operations (+,-,*,/,Dot,etc) as they are not meant to be used in that way. More formally, every Vec-like object has the following:

  • An overloaded bracket operator to access the components (which are index from 0 to n-1). Read-only Vec-like objects (as most will be) do not need to support assigning values to.
  • A ComponentType typedef.
  • A GetNumberOfComponents method.
  • An implementation of TypeTraits.
  • An implementation of VecTraits. This implementation does not have to define NUM_COMPONENTS. It also does not have to define SetComponent if it is read-only.

To make it easier to build Vec-like objects that conform to this contract, it will probably be useful to have a VecFacade class that implements the boilerplate.

Changes to Vec and VecTraits

Currently, VecTraits is built under the assumption that the size of the vector is known statically at compile time. However, our Vec-like objects do not behave this way. To accommodate this removing this assumption, the following changes should be made.

  • Vec should have a GetNumberOfComponents, which returns NUM_COMPONENTS.
  • Likewise, all VecTraits should define a GetNumberOfComponents method.
  • The ToVec method in VecTraits should be changed to CopyInto so that it can be used when the length of the Vec-like is not known. A CopyInto should also be added to Vec and all Vec-likes.
  • VecTraits should add a typedef named IsSizeStatic which is set to VecTraitsTagSizeStatic when the number of components is known at compile time and VecTraitsTagSizeVariable when the number of components is not known until runtime.

Examples of Vec-like objects

The claim of the design is that we can build Vec-like objects with sizes unknown at compile time but yet do not use dynamic allocation. Here are some examples of how that would work.

Structured Point To Cell Indices

Structured cells are the easy case because their size really is known at compile time. Thus, in this case we can just use a Vec class, which of course fulfills all the requirements of a Vec-like class.

Explicit Point To Cell Indices

A Vec-like object that gives the point indices for an explicit cell can be implemented by keeping providing a window into a connectivity array. It could look something like this.

template<typename ConnectivityPortalType>
class IndicesExplicit
  typedef typename ConnectivityPortalType::ValueType ComponentType;

  vtkm::IdComponent GetNumberOfComponents() const {
    return this->NumComponents;

  ComponentType operator[](vtkm::IdComponent index) const {
    return this->Connectivity.Get(this->Offset + index);

  ConnectivityPortalType Connectivity;
  vtkm::Id Offset;
  vtkm::IdComponent NumComponents;

Field Values

Get field values from the "from" topology elements (e.g. the points in a point to cell operation) requires a level of indirection of cell index to point index to field array. This can be implemented using a Vec-like that gets the point index (like that described above) and a reference to the field portal.

template<typename IndicesVecType, typename FieldPortalType>
class FieldFromElementVec
  typedef typename FieldPortalType::ValueType ComponentType;

  vtkm::IdComponent GetNumberOfComponents() const {
    return this->Indices.GetNumberOfComponents();

  ComponentType operator[](vtkm::IdComponent index) const {
    vtkm::Id fromElementIndex = this->Indices[index];
    return this->Field.Get(fromElementIndex);

  IndicesVecType Indices;
  FieldPortalType Field;

An interesting upshot of this implementation is that data values are loaded lazily.

SAND 2015-6547 O