Enable writing fields with Vecs of unknown length
Like VTK, VTK-m has an explicit way to represent vectors/tuples in an array to express things such as 3D coordinates. Although the memory layout is (typically) the same between the two, there is a subtle but important limitation in the way VTK-m represents them.
In VTK, the size of a "tuple" is selected at runtime. This allows the size of the tuples to easily be adjusted to whatever is needed. When using an array with a tuple size determined at runtime, small arrays are allocated to move data in and out of the array.
However, this approach does not work well on a GPU because dynamically
allocating memory of the appropriate size is problematic. Instead, VTK-m
takes a slightly different approach. VTK-m defines a simple templated type
named Vec
that represents a short vector of components with a length
defined at compile time. Because the length is defined at compile time,
operating on arrays of indeterminate type is problematic. Operating
directly on the data requires a separate compilation path for each vector
length.
When the size of the Vec
in a VTK-m array is not specifiable, then
creating all code paths for any possible array type is problematic. Thus,
VTK-m needs ways to operate on these arrays.
Fortunately, we have made some recent strides on making this possible for a variety of use cases, but some problems still exist. This issue will start with listing the features that are currently available and then list those that remain.
Features that are available
Several recent additions allow VTK-m algorithms to operate on arrays with
arbitrary Vec
lengths. The most important feature is the
ArrayExtractComponent
function that allows pulling a single component of
the array out without copying data and without knowing the length of the
Vec
s. This is used for multiple methods of UnknownArrayHandle
to
extract arrays of known type regardless of what kind of array is stored.
It is also possible to create new arrays that have the same vector size as
an input array. The NewInstance
, NewInstanceBasic
and
NewInstanceFloatBasic
methods of UnknownArrayHandle
all provide a means
of creating a new array with an unknown vector size although you need an
existing array with the target vector size already.
It is also usually straightforward to create a scalar array with the same
component type as that of an input array of Vec
s. This is usually done
after some sort of CastAndCall
determines the component type of the
input.
What is missing
There are some features that are still difficult or impossible with the current features.
Get extracted array with float fallback
As mentioned, UnknownArrayHandle
has a way of extracting a component from
an array knowing only the base component type of the array. This can be
used to get an "extracted array," and this in turn is used with a special
CastAndCall
to get arrays of any storage or Vec
length.
However, this CastAndCall
does not have a means for a float fallback.
This is less important than other CastAndCall
s as there are only about 10
possibilities. However, for complex operations it might be better to just
worry about a restricted set of types. Thus, it would be helpful to have a
CastAndCallWithExtractedArrayWithFloatFallback
. This mouthful of a method
would allow someone to provide a vtkm::List
of component types to try,
and would convert to vtkm::FloatDefault
if none of these worked.
Vec
length
Construct an array of arbitrary Currently, to create an array of Vec
s, you either need to know the size
of the Vec
s at compile time or have an array of the same Vec
size you
want. This can be problematic in some instances. For example, if a filter
were to create a field that had Vec
s with twice as many components as the
input (say, computing a confidence interval), that could not be done in a
single array. This also comes up a lot when importing data from other
sources such as when reading data from a file or converting from VTK.
The proposed solution would be to create a new type of ArrayHandle
that
is a cross between ArrayHandleGroupVec
and ArrayHandleGroupVecVariable
.
It would essentially behave like ArrayHandleGroupVecVariable
in that the
size of the Vec
s could be assigned at runtime, but it would limit the
array to having the same Vec
size for every entry. In this respect it is
like ArrayHandleGroupVec
but would not have to set the size at compile
time. We could call this class ArrayHandleGroupVecRuntime
(unless a
better name comes along).
We won't dwell on the implementation specifics of
ArrayHandleGroupVecRuntime
. The implementation will be very similar to
ArrayHandleGroupVecVariable
except that the offsets can be implied
implicitly.
Of course, simply creating an ArrayHandleGroupVecRuntime
does not really
solve the problem. Sure, you could create this array and put it in a field,
but we don't want to add yet another type of storage to compile into all
the algorithms in VTK-m. However, we can get around this problem by doing
some implicit conversions in UnknownArrayHandle
.
UnknownArrayHandle
already provides some implicit conversions when data
does not need to be copied. For example, if you ask an UnknownArrayHandle
for an ArrayHandleMultiplexer
, then it will return the array if it
matches any of the conditions of that array.
In the same way, we can create special checks for a basic ArrayHandle
.
When requesting an ArrayHandleBasic
, UnknownArrayHandle
can check if it
contains an ArrayHandleGroupVecRuntime
of the appropriate type and
length. It should then be possible to share Buffer
objects to transform
the data to the expected type.
Although a bit more tricky, we should be able to do the opposite as well.
That is, ask for an ArrayHandleGroupVecRuntime
and have it match any
ArrayHandleBasic
of the appropriate component type.