4.9. Fancy Array Handles
One of the features of using viskores::cont::ArrayHandle is that it hides the implementation and layout of the array behind a generic interface.
This gives us the opportunity to replace a simple C array with some custom definition of the data and the code using the viskores::cont::ArrayHandle is none the wiser.
This gives us the opportunity to implement fancy arrays that do more than simply look up a value in an array. For example, arrays can be augmented on the fly by mutating their indices or values. Or values could be computed directly from the index so that no storage is required for the array at all. Viskores provides many of the fancy arrays, which we explore in this section.
Did You Know?
One of the advantages of Viskores’s implementation of fancy arrays is that they can define whole arrays without actually storing and values.
For example, viskores::cont::ArrayHandleConstant, viskores::cont::ArrayHandleIndex, and viskores::cont::ArrayHandleCounting do not store data in any array in memory.
Rather, they construct the value for an index at runtime.
Likewise, arrays like viskores::cont::ArrayHandlePermutation construct new arrays from the values of other arrays without having to create a copy of the data.
Did You Know?
This chapter documents several array handle types that modify other array handles. Chapter 4.8 (Memory Layout of Array Handles) has several similar examples of modifying basic arrays to represent data in different layouts. The difference is that the fancy array handles in this chapter decorate other array handles of any type whereas those in Chapter 4.8 only decorate basic array handles. If you do not find the fancy array handle you are looking for here, you might try that chapter.
4.9.1. Constant Arrays
A constant array is a fancy array handle that has the same value in all of its entries. The constant array provides this array without actually using any memory.
Specifying a constant array in Viskores is straightforward.
Viskores has a class named viskores::cont::ArrayHandleConstant.
viskores::cont::ArrayHandleConstant is a templated class with a single template argument that is the type of value for each element in the array.
The constructor for viskores::cont::ArrayHandleConstant takes the value to provide by the array and the number of values the array should present.
The following example is a simple demonstration of the constant array handle.
-
template<typename T>
class ArrayHandleConstant : public viskores::cont::ArrayHandle<T, viskores::cont::StorageTagConstant> An array handle with a constant value.
ArrayHandleConstantis an implicit array handle with a constant value. A constant array handle is constructed by giving a value and an array length. The resulting array is of the given size with each entry the same value given in the constructor. The array is defined implicitly, so there it takes (almost) no memory.
1 // Create an array of 50 entries, all containing the number 3. This could be
2 // used, for example, to represent the sizes of all the polygons in a set
3 // where we know all the polygons are triangles.
4 viskores::cont::ArrayHandleConstant<viskores::Id> constantArray(3, 50);
The viskores/cont/ArrayHandleConstant.h header also contains the templated convenience function viskores::cont::make_ArrayHandleConstant() that takes a value and a size for the array.
This function can sometimes be used to avoid having to declare the full array type.
-
template<typename T>
viskores::cont::ArrayHandleConstant<T> viskores::cont::make_ArrayHandleConstant(T value, viskores::Id numberOfValues) make_ArrayHandleConstantis convenience function to generate an ArrayHandleImplicit.
1 // Create an array of 50 entries, all containing the number 3.
2 viskores::cont::make_ArrayHandleConstant(3, 50)
4.9.2. Viewing a Subset of an Array
An array handle view is a fancy array handle that returns a subset of an already existing array handle. The array handle view uses the same memory as the existing array handle the view was created from. This means that changes to the data in the array handle view will also change the data in the original array handle.
-
template<typename ArrayHandleType>
class ArrayHandleView : public viskores::cont::ArrayHandle<ArrayHandleType::ValueType, StorageTagView<ArrayHandleType::StorageTag>> Provided a windowed view into a
viskores::cont::ArrayHandle.ArrayHandleViewis a fancy array that wraps around anotherArrayHandleand reindexes it to provide access to a specified region of values in the array. This view is specified using the offset to the first index and the length of the entries to view.Public Functions
-
inline ArrayHandleView(const ArrayHandleType &array, viskores::Id startIndex, viskores::Id numValues)
Create an
ArrayHandleViewover a provided source array.- Parameters:
array – The source array to create a view from.
startIndex – The offset in
arrayto start the view.numValues – The number of values in the view.
-
inline ArrayHandleType GetSourceArray() const
Retrieve the full array being viewed.
-
inline ArrayHandleView(const ArrayHandleType &array, viskores::Id startIndex, viskores::Id numValues)
To use the viskores::cont::ArrayHandleView you must supply an viskores::cont::ArrayHandle to the viskores::cont::ArrayHandleView class constructor.
viskores::cont::ArrayHandleView is a templated class with a single template argument that is the viskores::cont::ArrayHandle type of the array that the view is being created from.
The constructor for viskores::cont::ArrayHandleView takes a target array, starting index, and length.
The following example shows a simple usage of the array handle view.
1 viskores::cont::ArrayHandle<viskores::Id> sourceArray;
2 viskores::cont::ArrayCopy(viskores::cont::ArrayHandleIndex(10), sourceArray);
3 // sourceArray has [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4
5 viskores::cont::ArrayHandleView<viskores::cont::ArrayHandle<viskores::Id>> viewArray(
6 sourceArray, 3, 5);
7 // viewArray has [3, 4, 5, 6, 7]
The viskores/cont/ArrayHandleView.h header contains a templated convenience function viskores::cont::make_ArrayHandleView() that takes a target array, index, and length.
-
template<typename ArrayHandleType>
ArrayHandleView<ArrayHandleType> viskores::cont::make_ArrayHandleView(const ArrayHandleType &array, viskores::Id startIndex, viskores::Id numValues) Construct a
viskores::cont::ArrayHandleViewfrom a source array.
1 viskores::cont::make_ArrayHandleView(sourceArray, 3, 5)
4.9.3. Counting Arrays
A counting array is a fancy array handle that provides a sequence of numbers. These fancy arrays can represent the data without actually using any memory.
Viskores provides two versions of a counting array.
The first version is an index array that provides a specialized but common form of a counting array called an index array.
An index array has values of type viskores::Id that start at 0 and count up by 1 (i.e., \(0, 1, 2, 3,\ldots\)).
The index array mirrors the array’s index.
-
class ArrayHandleIndex : public viskores::cont::ArrayHandle<viskores::Id, StorageTagIndex>
An implicit array handle containing the its own indices.
ArrayHandleIndexis an implicit array handle containing the values 0, 1, 2, 3,… to a specified size. Every value in the array is the same as the index to that value.
Specifying an index array in Viskores is done with a class named viskores::cont::ArrayHandleIndex.
The constructor for viskores::cont::ArrayHandleIndex takes the size of the array to create.
The following example is a simple demonstration of the index array handle.
1 // Create an array containing [0, 1, 2, 3, ..., 49].
2 viskores::cont::ArrayHandleIndex indexArray(50);
A viskores::cont::make_ArrayHandleIndex() convenience function is also available.
-
inline viskores::cont::ArrayHandleIndex viskores::cont::make_ArrayHandleIndex(viskores::Id length)
A convenience function for creating an ArrayHandleIndex.
It takes the size of the array and generates an array holding viskores::Id from [0, size - 1]
The viskores::cont::ArrayHandleCounting class provides a more general form of counting.
viskores::cont::ArrayHandleCounting is a templated class with a single template argument that is the type of value for each element in the array.
The constructor for viskores::cont::ArrayHandleCounting takes three arguments: the start value (used at index 0), the step from one value to the next, and the length of the array.
The following example is a simple demonstration of the counting array handle.
-
template<typename CountingValueType>
class ArrayHandleCounting : public viskores::cont::ArrayHandle<CountingValueType, viskores::cont::StorageTagCounting> ArrayHandleCounting is a specialization of ArrayHandle.
By default it contains a increment value, that is increment for each step between zero and the passed in length
1 // Create an array containing [-1.0, -0.9, -0.8, ..., 0.9, 1.0]
2 viskores::cont::ArrayHandleCounting<viskores::Float32> sampleArray(-1.0f, 0.1f, 21);
Did You Know?
In addition to being simpler to declare, viskores::cont::ArrayHandleIndex is slightly faster than viskores::cont::ArrayHandleCounting.
Thus, when applicable, you should prefer using viskores::cont::ArrayHandleIndex.
The viskores/cont/ArrayHandleCounting.h header also contains the templated convenience function viskores::cont::make_ArrayHandleCounting that also takes the start value, step, and length as arguments.
This function can sometimes be used to avoid having to declare the full array type.
-
template<typename CountingValueType>
viskores::cont::ArrayHandleCounting<CountingValueType> viskores::cont::make_ArrayHandleCounting(CountingValueType start, CountingValueType step, viskores::Id length) A convenience function for creating an ArrayHandleCounting.
It takes the value to start counting from and and the number of times to increment.
1 // Create an array containing [-1.0, -0.9, -0.8, ..., 0.9, 1.0]
2 viskores::cont::make_ArrayHandleCounting(-1.0f, 0.1f, 21)
There are no fundamental limits on how viskores::cont::ArrayHandleCounting counts.
For example, it is possible to count backwards.
1 // Create an array containing [49, 48, 47, 46, ..., 0].
2 viskores::cont::ArrayHandleCounting<viskores::Id> backwardIndexArray(49, -1, 50);
It is also possible to use viskores::cont::ArrayHandleCounting to make sequences of viskores::Vec values with piece-wise counting in each of the components.
1 // Create an array containg [(0,-3,75), (1,2,25), (3,7,-25)]
2 viskores::cont::make_ArrayHandleCounting(
3 viskores::make_Vec(0, -3, 75), viskores::make_Vec(1, 5, -50), 3)
4.9.4. Cast Arrays
A cast array is a fancy array that changes the type of the elements in an array. The cast array provides this re-typed array without actually copying or generating any data. Instead, casts are performed as the array is accessed.
Viskores has a class named viskores::cont::ArrayHandleCast to perform this implicit casting.
viskores::cont::ArrayHandleCast is a templated class with two template arguments.
The first argument is the type to cast values to.
The second argument is the type of the original viskores::cont::ArrayHandle.
The constructor to viskores::cont::ArrayHandleCast takes the viskores::cont::ArrayHandle to modify by casting.
-
template<typename T, typename ArrayHandleType>
class ArrayHandleCast : public viskores::cont::ArrayHandle<T, StorageTagCast<ArrayHandleType::ValueType, ArrayHandleType::StorageTag>> Cast the values of an array to the specified type, on demand.
ArrayHandleCast is a specialization of ArrayHandleTransform. Given an ArrayHandle and a type, it creates a new handle that returns the elements of the array cast to the specified type.
Public Functions
-
inline ArrayHandleCast(const viskores::cont::ArrayHandle<typename ArrayHandleType::ValueType, typename ArrayHandleType::StorageTag> &handle)
Construct an
ArrayHandleCastfrom a source array handle.
-
inline ArrayHandleType GetSourceArray() const
Returns the
ArrayHandlethat is being transformed.
-
inline ArrayHandleCast(const viskores::cont::ArrayHandle<typename ArrayHandleType::ValueType, typename ArrayHandleType::StorageTag> &handle)
1template<typename T>
2VISKORES_CONT void Foo(const std::vector<T>& inputData)
3{
4 viskores::cont::ArrayHandle<T> originalArray =
5 viskores::cont::make_ArrayHandle(inputData, viskores::CopyFlag::On);
6
7 viskores::cont::ArrayHandleCast<viskores::Float64, viskores::cont::ArrayHandle<T>>
8 castArray(originalArray);
The viskores/cont/ArrayHandleCast.h header also contains the templated convenience function viskores::cont::make_ArrayHandleCast() that constructs the cast array.
The first argument is the original viskores::cont::ArrayHandle original array to cast.
The optional second argument is of the type to cast to (or you can optionally specify the cast-to type as a template argument.
-
template<typename T, typename ArrayType>
detail::MakeArrayHandleCastImpl<T, typename ArrayType::ValueType, ArrayType>::ReturnType viskores::cont::make_ArrayHandleCast(const ArrayType &array, const T& = T()) make_ArrayHandleCastis convenience function to generate an ArrayHandleCast.
1 viskores::cont::make_ArrayHandleCast<viskores::Float64>(originalArray)
4.9.5. Discard Arrays
It is sometimes the case where you will want to run an operation in Viskores that fills values in two (or more) arrays, but you only want the values that are stored in one of the arrays. It is possible to allocate space for both arrays and then throw away the values that you do not want, but that is a waste of memory. It is also possible to rewrite the functionality to output only what you want, but that is a poor use of developer time.
To solve this problem easily, Viskores provides viskores::cont::ArrayHandleDiscard.
This array behaves similar to a regular viskores::cont::ArrayHandle in that it can be “allocated” and has size, but any values that are written to it are immediately discarded.
viskores::cont::ArrayHandleDiscard takes up no memory.
-
template<typename ValueType_>
class ArrayHandleDiscard : public viskores::cont::ArrayHandle<ValueType, StorageTag> ArrayHandleDiscard is a write-only array that discards all data written to it.
This can be used to save memory when a filter provides optional outputs that are not needed.
1template<typename InputArrayType, typename OutputArrayType1, typename OutputArrayType2>
2VISKORES_CONT void DoFoo(InputArrayType input,
3 OutputArrayType1 output1,
4 OutputArrayType2 output2);
5
6template<typename InputArrayType>
7VISKORES_CONT inline viskores::cont::ArrayHandle<viskores::FloatDefault> DoBar(
8 InputArrayType input)
9{
10 VISKORES_IS_ARRAY_HANDLE(InputArrayType);
11
12 viskores::cont::ArrayHandle<viskores::FloatDefault> keepOutput;
13
14 viskores::cont::ArrayHandleDiscard<viskores::FloatDefault> discardOutput;
15
16 DoFoo(input, keepOutput, discardOutput);
17
18 return keepOutput;
19}
4.9.6. Permuted Arrays
A permutation array is a fancy array handle that reorders the elements in an array. Elements in the array can be skipped over or replicated. The permutation array provides this reordered array without actually coping any data. Instead, indices are adjusted as the array is accessed.
Specifying a permutation array in Viskores is straightforward.
Viskores has a class named viskores::cont::ArrayHandlePermutation that takes two arrays: an array of values and an array of indices that maps an index in the permutation to an index of the original values.
The index array is specified first.
The following example is a simple demonstration of the permutation array handle.
-
template<typename IndexArrayHandleType, typename ValueArrayHandleType>
class ArrayHandlePermutation : public viskores::cont::ArrayHandle<ValueArrayHandleType::ValueType, viskores::cont::StorageTagPermutation<IndexArrayHandleType::StorageTag, ValueArrayHandleType::StorageTag>> Implicitly permutes the values in an array.
ArrayHandlePermutation is a specialization of ArrayHandle. It takes two delegate array handles: an array of indices and an array of values. The array handle created contains the values given permuted by the indices given. So for a given index i, ArrayHandlePermutation looks up the i-th value in the index array to get permuted index j and then gets the j-th value in the value array. This index permutation is done on the fly rather than creating a copy of the array.
An ArrayHandlePermutation can be used for either input or output. However, if used for output the array must be pre-allocated. That is, the indices must already be established and the values must have an allocation large enough to accommodate the indices. An output ArrayHandlePermutation will only have values changed. The indices are never changed.
When using ArrayHandlePermutation great care should be taken to make sure that every index in the index array points to a valid position in the value array. Otherwise, access validations will occur. Also, be wary of duplicate indices that point to the same location in the value array. For input arrays, this is fine. However, this could result in unexpected results for using as output and is almost certainly wrong for using as in-place.
Public Functions
-
inline ArrayHandlePermutation(const IndexArrayHandleType &indexArray, const ValueArrayHandleType &valueArray)
Construct a permuation array with index and value arrays.
-
inline IndexArrayHandleType GetIndexArray() const
Return the array used for indices.
The index array provides how indices get permuted. When a value is retrieved from an
ArrayHandlePermutation, an index is retrived from this index array, and this new index is used to retrieve a value from the value array.
-
inline ValueArrayHandleType GetValueArray() const
Return the array used for values.
The index array provides how indices get permuted. When a value is retrieved from an
ArrayHandlePermutation, an index is retrived from this index array, and this new index is used to retrieve a value from the value array.
-
inline ArrayHandlePermutation(const IndexArrayHandleType &indexArray, const ValueArrayHandleType &valueArray)
1 using IdArrayType = viskores::cont::ArrayHandle<viskores::Id>;
2 using IdPortalType = IdArrayType::WritePortalType;
3
4 using ValueArrayType = viskores::cont::ArrayHandle<viskores::Float64>;
5 using ValuePortalType = ValueArrayType::WritePortalType;
6
7 // Create array with values [0.0, 0.1, 0.2, 0.3]
8 ValueArrayType valueArray;
9 valueArray.Allocate(4);
10 ValuePortalType valuePortal = valueArray.WritePortal();
11 valuePortal.Set(0, 0.0);
12 valuePortal.Set(1, 0.1);
13 valuePortal.Set(2, 0.2);
14 valuePortal.Set(3, 0.3);
15
16 // Use ArrayHandlePermutation to make an array = [0.3, 0.0, 0.1].
17 IdArrayType idArray1;
18 idArray1.Allocate(3);
19 IdPortalType idPortal1 = idArray1.WritePortal();
20 idPortal1.Set(0, 3);
21 idPortal1.Set(1, 0);
22 idPortal1.Set(2, 1);
23 viskores::cont::ArrayHandlePermutation<IdArrayType, ValueArrayType> permutedArray1(
24 idArray1, valueArray);
25
26 // Use ArrayHandlePermutation to make an array = [0.1, 0.2, 0.2, 0.3, 0.0]
27 IdArrayType idArray2;
28 idArray2.Allocate(5);
29 IdPortalType idPortal2 = idArray2.WritePortal();
30 idPortal2.Set(0, 1);
31 idPortal2.Set(1, 2);
32 idPortal2.Set(2, 2);
33 idPortal2.Set(3, 3);
34 idPortal2.Set(4, 0);
35 viskores::cont::ArrayHandlePermutation<IdArrayType, ValueArrayType> permutedArray2(
36 idArray2, valueArray);
The viskores/cont/ArrayHandlePermutation.h header also contains the templated convenience function viskores::cont::make_ArrayHandlePermutation() that takes instances of the index and value array handles and returns a permutation array.
This function can sometimes be used to avoid having to declare the full array type.
-
template<typename IndexArrayHandleType, typename ValueArrayHandleType>
viskores::cont::ArrayHandlePermutation<IndexArrayHandleType, ValueArrayHandleType> viskores::cont::make_ArrayHandlePermutation(IndexArrayHandleType indexArray, ValueArrayHandleType valueArray) make_ArrayHandleTransform is convenience function to generate an ArrayHandleTransform.
It takes in an ArrayHandle and a functor to apply to each element of the Handle.
1 viskores::cont::make_ArrayHandlePermutation(idArray, valueArray)
Common Errors
When using an viskores::cont::ArrayHandlePermutation, take care that all the provided indices in the index array point to valid locations in the values array.
Bad indices can cause reading from or writing to invalid memory locations, which can be difficult to debug.
Also, be wary about having duplicate indices, which means that multiple array entries point to the same memory location.
This will work fine when using the array as input, but will cause a dangerous race condition if used as an output.
Did You Know?
You can write to a viskores::cont::ArrayHandlePermutation by, for example, using it as an output array.
Writes to the viskores::cont::ArrayHandlePermutation will go to the respective location in the source array.
However, viskores::cont::ArrayHandlePermutation cannot be resized.
4.9.7. Zipped Arrays
A zip array is a fancy array handle that combines two arrays of the same size to pair up the corresponding values.
Each element in the zipped array is a viskores::Pair containing the values of the two respective arrays.
These pairs are not stored in their own memory space.
Rather, the pairs are generated as the array is used.
Writing a pair to the zipped array writes the values in the two source arrays.
Specifying a zipped array in Viskores is straightforward.
Viskores has a class named viskores::cont::ArrayHandleZip that takes the two arrays providing values for the first and second entries in the pairs.
The following example is a simple demonstration of creating a zip array handle.
-
template<typename FirstHandleType, typename SecondHandleType>
class ArrayHandleZip : public viskores::cont::ArrayHandle<ValueType, Tag> ArrayHandleZip is a specialization of ArrayHandle.
It takes two delegate array handle and makes a new handle that access the corresponding entries in these arrays as a pair.
Public Functions
-
inline ArrayHandleZip(const FirstHandleType &firstArray, const SecondHandleType &secondArray)
Create
ArrayHandleZipwith two arrays.
-
inline FirstHandleType GetFirstArray() const
Returns the the array for the first part of the zip pair.
-
inline SecondHandleType GetSecondArray() const
Returns the the array for the second part of the zip pair.
-
inline ArrayHandleZip(const FirstHandleType &firstArray, const SecondHandleType &secondArray)
1 using ArrayType1 = viskores::cont::ArrayHandle<viskores::Id>;
2 using PortalType1 = ArrayType1::WritePortalType;
3
4 using ArrayType2 = viskores::cont::ArrayHandle<viskores::Float64>;
5 using PortalType2 = ArrayType2::WritePortalType;
6
7 // Create an array of viskores::Id with values [3, 0, 1]
8 ArrayType1 array1;
9 array1.Allocate(3);
10 PortalType1 portal1 = array1.WritePortal();
11 portal1.Set(0, 3);
12 portal1.Set(1, 0);
13 portal1.Set(2, 1);
14
15 // Create a second array of viskores::Float32 with values [0.0, 0.1, 0.2]
16 ArrayType2 array2;
17 array2.Allocate(3);
18 PortalType2 portal2 = array2.WritePortal();
19 portal2.Set(0, 0.0);
20 portal2.Set(1, 0.1);
21 portal2.Set(2, 0.2);
22
23 // Zip the two arrays together to create an array of
24 // viskores::Pair<viskores::Id, viskores::Float64> with values [(3,0.0), (0,0.1), (1,0.2)]
25 viskores::cont::ArrayHandleZip<ArrayType1, ArrayType2> zipArray(array1, array2);
The viskores/cont/ArrayHandleZip.h header also contains the templated convenience function viskores::cont::make_ArrayHandleZip() that takes instances of the two array handles and returns a zip array.
This function can sometimes be used to avoid having to declare the full array type.
-
template<typename FirstHandleType, typename SecondHandleType>
viskores::cont::ArrayHandleZip<FirstHandleType, SecondHandleType> viskores::cont::make_ArrayHandleZip(const FirstHandleType &first, const SecondHandleType &second) A convenience function for creating an ArrayHandleZip.
It takes the two arrays to be zipped together.
1 viskores::cont::make_ArrayHandleZip(array1, array2)
4.9.8. Coordinate System Arrays
Many of the data structures we use in Viskores are described in a 3D coordinate system.
Although, as we will see in Chapter 2.4 (Data Sets), we can use any viskores::cont::ArrayHandle to store point coordinates, including a raw array of 3D vectors, there are some common patterns for point coordinates that we can use specialized arrays to better represent the data.
There are two fancy array handles that each handle a special form of coordinate system.
The first such array handle is viskores::cont::ArrayHandleUniformPointCoordinates, which represents a uniform sampling of space.
The constructor for viskores::cont::ArrayHandleUniformPointCoordinates takes three arguments.
The first argument is a viskores::Id3 that specifies the number of samples in the \(x\), \(y\), and \(z\) directions.
The second argument, which is optional, specifies the origin (the location of the first point at the lower left corner).
If not specified, the origin is set to \([0,0,0]\).
The third argument, which is also optional, specifies the distance between samples in the \(x\), \(y\), and \(z\) directions.
If not specified, the spacing is set to 1 in each direction.
-
class ArrayHandleUniformPointCoordinates : public viskores::cont::ArrayHandle<viskores::Vec3f, viskores::cont::StorageTagUniformPoints>
ArrayHandleUniformPointCoordinates is a specialization of ArrayHandle.
It contains the information necessary to compute the point coordinates in a uniform orthogonal grid (extent, origin, and spacing) and implicitly computes these coordinates in its array portal.
Public Functions
-
ArrayHandleUniformPointCoordinates(viskores::Id3 dimensions, ValueType origin = ValueType(0.0f, 0.0f, 0.0f), ValueType spacing = ValueType(1.0f, 1.0f, 1.0f))
Create an
ArrayHandleUniformPointCoordinateswith the given specifications.
-
ArrayHandleUniformPointCoordinates(viskores::Id3 dimensions, ValueType origin = ValueType(0.0f, 0.0f, 0.0f), ValueType spacing = ValueType(1.0f, 1.0f, 1.0f))
1 // Create a set of point coordinates for a uniform grid in the space between
2 // -5 and 5 in the x direction and -3 and 3 in the y and z directions. The
3 // uniform sampling is spaced in 0.08 unit increments in the x direction (for
4 // 126 samples), 0.08 unit increments in the y direction (for 76 samples) and
5 // 0.24 unit increments in the z direction (for 26 samples). That makes
6 // 248,976 values in the array total.
7 viskores::cont::ArrayHandleUniformPointCoordinates uniformCoordinates(
8 viskores::Id3(126, 76, 26),
9 viskores::Vec3f{ -5.0f, -3.0f, -3.0f },
10 viskores::Vec3f{ 0.08f, 0.08f, 0.24f });
The second fancy array handle for special coordinate systems is viskores::cont::ArrayHandleCartesianProduct, which represents a rectilinear sampling of space where the samples are axis aligned but have variable spacing.
Sets of coordinates of this type are most efficiently represented by having a separate array for each component of the axis, and then for each \([i,j,k]\) index of the array take the value for each component from each array using the respective index.
This is equivalent to performing a Cartesian product on the arrays.
-
template<typename FirstHandleType, typename SecondHandleType, typename ThirdHandleType>
class ArrayHandleCartesianProduct : public viskores::cont::ArrayHandle<ValueType, Tag> ArrayHandleCartesianProduct is a specialization of ArrayHandle.
It takes two delegate array handle and makes a new handle that access the corresponding entries in these arrays as a pair.
Public Functions
-
inline ArrayHandleCartesianProduct(const FirstHandleType &firstArray, const SecondHandleType &secondArray, const ThirdHandleType &thirdArray)
Construct an
ArrayHandleCartesianProductgiven arrays for the coordinates in the x, y, and z diretions.
-
inline ~ArrayHandleCartesianProduct()
Implemented so that it is defined exclusively in the control environment.
If there is a separate device for the execution environment (for example, with CUDA), then the automatically generated destructor could be created for all devices, and it would not be valid for all devices.
-
inline FirstHandleType GetFirstArray() const
Get the array for the coordinates in the x direction.
-
inline SecondHandleType GetSecondArray() const
Get the array for the coordinates in the y direction.
-
inline ThirdHandleType GetThirdArray() const
Get the array for the coordinates in the z direction.
-
inline ArrayHandleCartesianProduct(const FirstHandleType &firstArray, const SecondHandleType &secondArray, const ThirdHandleType &thirdArray)
viskores::cont::ArrayHandleCartesianProduct is a templated class.
It has three template parameters, which are the types of the arrays used for the \(x\), \(y\), and \(z\) axes.
The constructor for viskores::cont::ArrayHandleCartesianProduct takes the three arrays.
1 using AxisArrayType = viskores::cont::ArrayHandle<viskores::Float32>;
2 using AxisPortalType = AxisArrayType::WritePortalType;
3
4 // Create array for x axis coordinates with values [0.0, 1.1, 5.0]
5 AxisArrayType xAxisArray;
6 xAxisArray.Allocate(3);
7 AxisPortalType xAxisPortal = xAxisArray.WritePortal();
8 xAxisPortal.Set(0, 0.0f);
9 xAxisPortal.Set(1, 1.1f);
10 xAxisPortal.Set(2, 5.0f);
11
12 // Create array for y axis coordinates with values [0.0, 2.0]
13 AxisArrayType yAxisArray;
14 yAxisArray.Allocate(2);
15 AxisPortalType yAxisPortal = yAxisArray.WritePortal();
16 yAxisPortal.Set(0, 0.0f);
17 yAxisPortal.Set(1, 2.0f);
18
19 // Create array for z axis coordinates with values [0.0, 0.5]
20 AxisArrayType zAxisArray;
21 zAxisArray.Allocate(2);
22 AxisPortalType zAxisPortal = zAxisArray.WritePortal();
23 zAxisPortal.Set(0, 0.0f);
24 zAxisPortal.Set(1, 0.5f);
25
26 // Create point coordinates for a "rectilinear grid" with axis-aligned points
27 // with variable spacing by taking the Cartesian product of the three
28 // previously defined arrays. This generates the following 3x2x2 = 12 values:
29 //
30 // [0.0, 0.0, 0.0], [1.1, 0.0, 0.0], [5.0, 0.0, 0.0],
31 // [0.0, 2.0, 0.0], [1.1, 2.0, 0.0], [5.0, 2.0, 0.0],
32 // [0.0, 0.0, 0.5], [1.1, 0.0, 0.5], [5.0, 0.0, 0.5],
33 // [0.0, 2.0, 0.5], [1.1, 2.0, 0.5], [5.0, 2.0, 0.5]
34 viskores::cont::
35 ArrayHandleCartesianProduct<AxisArrayType, AxisArrayType, AxisArrayType>
36 rectilinearCoordinates(xAxisArray, yAxisArray, zAxisArray);
The viskores/cont/ArrayHandleCartesianProduct.h header also contains the templated convenience function viskores::cont::make_ArrayHandleCartesianProduct() that takes the three axis arrays and returns an array of the Cartesian product.
This function can sometimes be used to avoid having to declare the full array type.
-
template<typename FirstHandleType, typename SecondHandleType, typename ThirdHandleType>
viskores::cont::ArrayHandleCartesianProduct<FirstHandleType, SecondHandleType, ThirdHandleType> viskores::cont::make_ArrayHandleCartesianProduct(const FirstHandleType &first, const SecondHandleType &second, const ThirdHandleType &third) A convenience function for creating an ArrayHandleCartesianProduct.
It takes the two arrays to be zipped together.
1 viskores::cont::make_ArrayHandleCartesianProduct(xAxisArray, yAxisArray, zAxisArray)
Did You Know?
These specialized arrays for coordinate systems greatly reduce the code duplication in Viskores. Most scientific visualization systems need separate implementations of algorithms for uniform, rectilinear, and unstructured grids. But in Viskores an algorithm can be written once and then applied to all these different grid structures by using these specialized array handles and letting the compiler’s templates optimize the code.
Did You Know?
The special array handles in this section are designed to represent point coordinates in particular, common configurations.
However, the array for a viskores::cont::CoordinateSystem does not have to be one of these arrays.
For example, it is common to use a viskores::cont::ArrayHandleBasic to represent points in general position.
4.9.9. Composite Vector Arrays
A composite vector array is a fancy array handle that combines two to four arrays of the same size and value type and combines their corresponding values to form a viskores::Vec.
A composite vector array is similar in nature to a zipped array (described in Section 4.9.7 (Zipped Arrays)) except that values are combined into viskores::Vec’s instead of viskores::Pair’s.
The composite vector array is also similar to a structure of arrays (described in Section 4.8.2 (Structure of Arrays)) except that any type of array handles can be used for the components rather than a basic array handle.
The created viskores::Vec’s are not stored in their own memory space.
Rather, the viskores::Vec’s are generated as the array is used.
Writing viskores::Vec’s to the composite vector array writes values into the components of the source arrays.
A composite vector array can be created using the viskores::cont::ArrayHandleCompositeVector class.
This class has a variadic template argument that is a “signature” for the arrays to be combined.
The constructor for viskores::cont::ArrayHandleCompositeVector takes instances of the array handles to combine.
-
template<typename ...ArrayTs>
class ArrayHandleCompositeVector : public viskores::cont::ArrayHandle<internal::CompositeVectorTraits<ArrayTs...>::ValueType, internal::CompositeVectorTraits<ArrayTs...>::StorageTag> An
ArrayHandlethat combines components from other arrays.ArrayHandleCompositeVectoris a specialization ofArrayHandlethat derives its content from other arrays. It takes any number of single-componentArrayHandleobjects and mimics an array that contains vectors with components that come from these delegate arrays.The easiest way to create and type an
ArrayHandleCompositeVectoris to use themake_ArrayHandleCompositeVectorfunctions.The
ArrayHandleExtractComponentclass may be helpful when a desired component is part of anArrayHandlewith aviskores::VecValueType.If you are attempted to combine components that you know are stored in basic
ArrayHandles, consider usingArrayHandleSOAinstead.Public Functions
-
inline ArrayHandleCompositeVector(const ArrayTs&... arrays)
Construct an
ArrayHandleCompositeVectorfrom a set of component vectors.
-
inline viskores::Tuple<ArrayTs...> GetArrayTuple() const
Return the arrays of all of the components in a
viskores::Tupleobject.
-
inline ArrayHandleCompositeVector(const ArrayTs&... arrays)
1 // Create an array with [0, 1, 2, 3, 4]
2 using ArrayType1 = viskores::cont::ArrayHandleIndex;
3 ArrayType1 array1(5);
4
5 // Create an array with [3, 1, 4, 1, 5]
6 using ArrayType2 = viskores::cont::ArrayHandle<viskores::Id>;
7 ArrayType2 array2;
8 array2.Allocate(5);
9 ArrayType2::WritePortalType arrayPortal2 = array2.WritePortal();
10 arrayPortal2.Set(0, 3);
11 arrayPortal2.Set(1, 1);
12 arrayPortal2.Set(2, 4);
13 arrayPortal2.Set(3, 1);
14 arrayPortal2.Set(4, 5);
15
16 // Create an array with [2, 7, 1, 8, 2]
17 using ArrayType3 = viskores::cont::ArrayHandle<viskores::Id>;
18 ArrayType3 array3;
19 array3.Allocate(5);
20 ArrayType2::WritePortalType arrayPortal3 = array3.WritePortal();
21 arrayPortal3.Set(0, 2);
22 arrayPortal3.Set(1, 7);
23 arrayPortal3.Set(2, 1);
24 arrayPortal3.Set(3, 8);
25 arrayPortal3.Set(4, 2);
26
27 // Create an array with [0, 0, 0, 0]
28 using ArrayType4 = viskores::cont::ArrayHandleConstant<viskores::Id>;
29 ArrayType4 array4(0, 5);
30
31 // Use ArrayhandleCompositeVector to create the array
32 // [(0,3,2,0), (1,1,7,0), (2,4,1,0), (3,1,8,0), (4,5,2,0)].
33 using CompositeArrayType = viskores::cont::
34 ArrayHandleCompositeVector<ArrayType1, ArrayType2, ArrayType3, ArrayType4>;
35 CompositeArrayType compositeArray(array1, array2, array3, array4);
The viskores/cont/ArrayHandleCompositeVector.h header also contains the templated convenience function viskores::cont::make_ArrayHandleCompositeVector() which takes a variable number of array handles and returns an viskores::cont::ArrayHandleCompositeVector.
This function can sometimes be used to avoid having to declare the full array type.
-
template<typename ...ArrayTs>
ArrayHandleCompositeVector<ArrayTs...> viskores::cont::make_ArrayHandleCompositeVector(const ArrayTs&... arrays) Create a composite vector array from other arrays.
1 viskores::cont::make_ArrayHandleCompositeVector(array1, array2, array3, array4)
4.9.10. Extract Component Arrays
Component extraction allows access to a single component of an viskores::cont::ArrayHandle with a viskores::Vec as the viskores::cont::ArrayHandle::ValueType.
viskores::cont::ArrayHandleExtractComponent allows one component of a vector array to be extracted without creating a copy of the data.
viskores::cont::ArrayHandleExtractComponent can also be combined with viskores::cont::ArrayHandleCompositeVector (described in Section 4.9.9 (Composite Vector Arrays)) to arbitrarily stitch several components from multiple arrays together.
-
template<typename ArrayHandleType>
class ArrayHandleExtractComponent : public viskores::cont::ArrayHandle<viskores::VecTraits<ArrayHandleType::ValueType>::ComponentType, StorageTagExtractComponent<ArrayHandleType>> A fancy
ArrayHandlethat turns a vector array into a scalar array by slicing out a single component of each vector.ArrayHandleExtractComponentis a specialization ofArrayHandle. It takes an inputArrayHandlewith aviskores::VecValueTypeand a component index and uses this information to expose a scalar array consisting of the specified component across all vectors in the inputArrayHandle. So for a given index i,ArrayHandleExtractComponentlooks up the i-thviskores::Vecin the index array and reads or writes to the specified component, leave all other components unmodified. This is done on the fly rather than creating a copy of the array.Public Functions
-
inline ArrayHandleExtractComponent(const ArrayHandleType &array, viskores::IdComponent component)
Construct an
ArrayHandleExtractComponentwith a given array and component.
-
inline viskores::IdComponent GetComponent() const
Get the component index being extracted from the source array.
-
inline ArrayHandleType GetArray() const
Get the source array of
Vecs to get a component out of.
-
inline ArrayHandleExtractComponent(const ArrayHandleType &array, viskores::IdComponent component)
As a simple example, consider an viskores::cont::ArrayHandle containing 3D coordinates for a collection of points and a filter that only operates on the points’ elevations (Z, in this example).
We can easily create the elevation array on-the-fly without allocating a new array as in the following example.
viskores::Vec’s in an array with viskores::cont::ArrayHandleExtractComponent. 1 using ValueArrayType = viskores::cont::ArrayHandle<viskores::Vec3f_64>;
2
3 // Create array with values [ (0.0, 0.1, 0.2), (1.0, 1.1, 1.2), (2.0, 2.1, 2.2) ]
4 ValueArrayType valueArray;
5 valueArray.Allocate(3);
6 auto valuePortal = valueArray.WritePortal();
7 valuePortal.Set(0, viskores::make_Vec(0.0, 0.1, 0.2));
8 valuePortal.Set(1, viskores::make_Vec(1.0, 1.1, 1.2));
9 valuePortal.Set(2, viskores::make_Vec(2.0, 2.1, 2.2));
10
11 // Use ArrayHandleExtractComponent to make an array = [1.3, 2.3, 3.3].
12 viskores::cont::ArrayHandleExtractComponent<ValueArrayType> extractedComponentArray(
13 valueArray, 2);
The viskores/cont/ArrayHandleExtractComponent.h header also contains the templated convenience function viskores::cont::make_ArrayHandleExtractComponent() that takes an viskores::cont::ArrayHandle of viskores::Vec’s and viskores::IdComponent which returns an appropriately typed viskores::cont::ArrayHandleExtractComponent containing the values for a specified component.
The index of the component to extract is provided as an argument to viskores::cont::make_ArrayHandleExtractComponent(), which is required.
The use of viskores::cont::make_ArrayHandleExtractComponent() can be used to avoid having to declare the full array type.
-
template<typename ArrayHandleType>
ArrayHandleExtractComponent<ArrayHandleType> viskores::cont::make_ArrayHandleExtractComponent(const ArrayHandleType &array, viskores::IdComponent component) make_ArrayHandleExtractComponentis convenience function to generate anArrayHandleExtractComponent.
1 viskores::cont::make_ArrayHandleExtractComponent(valueArray, 2)
Did You Know?
An alternate way to extract a component from a viskores::cont::ArrayHandle is to use the viskores::cont::ArrayExtractComponent() function.
Rather than wrap a viskores::cont::ArrayHandleExtractComponent around the target array, it converts the array into a viskores::cont::ArrayHandleStride, as described in Section 4.8.3 (Strided Arrays).
This can be advantageous when trying to unify the storage type of different array types, but can work poorly for some array types.
4.9.11. Swizzle Arrays
It is often useful to reorder or remove specific components from an viskores::cont::ArrayHandle with a viskores::Vec viskores::cont::ArrayHandle::ValueType.
viskores::cont::ArrayHandleSwizzle provides an easy way to accomplish this.
The constructor of viskores::cont::ArrayHandleSwizzle specifies a “component map,” which defines the swizzle operation.
This map consists of the components from the input viskores::cont::ArrayHandle, which will be exposed in the viskores::cont::ArrayHandleSwizzle.
For instance, constructing viskores::cont::ArrayHandleSwizzle<Some3DArrayType, 3> with viskores::IdComponent3(0, 2, 1) as the second constructor argument will allow access to a 3D array, but with the Y and Z components exchanged.
This rearrangement does not create a copy, and occurs on-the-fly as data are accessed through the viskores::cont::ArrayHandleSwizzle’s portal.
This fancy array handle can also be used to eliminate unnecessary components from an viskores::cont::ArrayHandle’s data, as shown below.
-
template<typename ArrayHandleType, viskores::IdComponent OutSize>
class ArrayHandleSwizzle : public viskores::cont::ArrayHandleTransform<ArrayHandleType, SwizzleFunctor, InverseSwizzleFunctor> Swizzle the components of the values in an
ArrayHandle.Given an
ArrayHandlewithVecvalues,ArrayHandleSwizzleallows you to reorder the components of all theVecvalues. This reordering is done in place, so the array does not have to be duplicated.The resulting array does not have to contain all of the components of the input. For example, you could use
ArrayHandleSwizzleto drop one of the components of each vector. However, if you do that, then the swizzled array is read-only. If there is a 1:1 map from input components to output components, writing to the array will be enabled.The swizzle map given to
ArrayHandleSwizzlemust comprise valid component indices (between 0 and number of components - 1). Also, the component indices should not be repeated, particularly if you expect to write to the array. These conditions are not checked.Public Functions
-
inline ArrayHandleSwizzle(const ArrayHandleType &array, const MapType &map)
Construct an
ArrayHandleSwizzlewith a source array and a swizzle map.The swizzle map is a
viskores::Veccontainingviskores::IdComponentcomponents and sized to the number of components in the array. Each value in the map specifies from which component of the input the corresponding component of the output should come from.
-
inline ArrayHandleSwizzle(const ArrayHandleType &array, const MapType &map)
viskores::Vec’s in an array with viskores::cont::ArrayHandleSwizzle. 1 using ValueArrayType = viskores::cont::ArrayHandle<viskores::Vec4f_64>;
2
3 // Create array with values
4 // [ (0.0, 0.1, 0.2, 0.3), (1.0, 1.1, 1.2, 1.3), (2.0, 2.1, 2.2, 2.3) ]
5 ValueArrayType valueArray;
6 valueArray.Allocate(3);
7 auto valuePortal = valueArray.WritePortal();
8 valuePortal.Set(0, viskores::make_Vec(0.0, 0.1, 0.2, 0.3));
9 valuePortal.Set(1, viskores::make_Vec(1.0, 1.1, 1.2, 1.3));
10 valuePortal.Set(2, viskores::make_Vec(2.0, 2.1, 2.2, 2.3));
11
12 // Use ArrayHandleSwizzle to make an array of Vec-3 with x,y,z,w swizzled to z,x,w
13 // [ (0.2, 0.0, 0.3), (1.2, 1.0, 1.3), (2.2, 2.0, 2.3) ]
14 viskores::cont::ArrayHandleSwizzle<ValueArrayType, 3> swizzledArray(
15 valueArray, viskores::IdComponent3(2, 0, 3));
The viskores/cont/ArrayHandleSwizzle.h header also contains the templated convenience function viskores::cont::make_ArrayHandleSwizzle() that takes an viskores::cont::ArrayHandle of viskores::Vec’s and returns an appropriately typed viskores::cont::ArrayHandleSwizzle containing swizzled vectors.
The use of viskores::cont::make_ArrayHandleSwizzle() can be used to avoid having to declare the full array type.
-
template<typename ArrayHandleType, viskores::IdComponent OutSize>
ArrayHandleSwizzle<ArrayHandleType, OutSize> viskores::cont::make_ArrayHandleSwizzle(const ArrayHandleType &array, const viskores::Vec<viskores::IdComponent, OutSize> &map) Construct an
ArrayHandleSwizzlefrom a provided array and swizzle map.The swizzle map is a
viskores::Veccontainingviskores::IdComponentcomponents and sized to the number of components in the array. Each value in the map specifies from which component of the input the corresponding component of the output should come from.
-
template<typename ArrayHandleType, typename ...SwizzleIndexTypes>
auto viskores::cont::make_ArrayHandleSwizzle(const ArrayHandleType &array, viskores::IdComponent swizzleIndex0, SwizzleIndexTypes... swizzleIndices) Construct an
ArrayHandleSwizzlefrom a provided array and swizzle map.The swizzle map is specified as independent function parameters after the array. Each value in the map specifies from which component of the input the corresponding component of the output should come from.
1 viskores::cont::make_ArrayHandleSwizzle(valueArray, 2, 0, 3)
4.9.12. Grouped Vector Arrays
A grouped vector array is a fancy array handle that groups consecutive values of an array together to form a viskores::Vec.
The source array must be of a length that is divisible by the requested viskores::Vec size.
The created viskores::Vec’s are not stored in their own memory space.
Rather, the viskores::Vec’s are generated as the array is used.
Writing viskores::Vec’s to the grouped vector array writes values into the the source array.
A grouped vector array is created using the viskores::cont::ArrayHandleGroupVec class.
This templated class has two template arguments.
The first argument is the type of array being grouped and the second argument is an integer specifying the size of the viskores::Vec’s to create (the number of values to group together).
-
template<typename ComponentsArrayHandleType, viskores::IdComponent NUM_COMPONENTS>
class ArrayHandleGroupVec : public viskores::cont::ArrayHandle<viskores::Vec<ComponentsArrayHandleType::ValueType, NUM_COMPONENTS>, viskores::cont::StorageTagGroupVec<ComponentsArrayHandleType::StorageTag, NUM_COMPONENTS>> Fancy array handle that groups values into vectors.
It is sometimes the case that an array is stored such that consecutive entries are meant to form a group. This fancy array handle takes an array of values and a size of groups and then groups the consecutive values stored in a
viskores::Vec.For example, if you have an array handle with the six values 0,1,2,3,4,5 and give it to a
ArrayHandleGroupVecwith the number of components set to 3, you get an array that looks like it contains two values ofviskores::Vecof size 3 with the data [0,1,2], [3,4,5].The array of components should have a number of values that divides evenly with the size of the
viskores::Vec. If the components array does not divide evenly intoviskores::Vecs, then a warning will be logged and the extra component values will be ignored.Public Functions
-
inline ArrayHandleGroupVec(const ComponentsArrayHandleType &componentsArray)
Construct an
ArrayHandleGroupVecwith a provided components array.
-
inline ComponentsArrayHandleType GetComponentsArray() const
Retrieve the components array being grouped.
-
inline ArrayHandleGroupVec(const ComponentsArrayHandleType &componentsArray)
1 // Create an array containing [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
2 using ArrayType = viskores::cont::ArrayHandleIndex;
3 ArrayType sourceArray(12);
4
5 // Create an array containing [(0,1), (2,3), (4,5), (6,7), (8,9), (10,11)]
6 viskores::cont::ArrayHandleGroupVec<ArrayType, 2> vec2Array(sourceArray);
7
8 // Create an array containing [(0,1,2), (3,4,5), (6,7,8), (9,10,11)]
9 viskores::cont::ArrayHandleGroupVec<ArrayType, 3> vec3Array(sourceArray);
The viskores/cont/ArrayHandleGroupVec.h header also contains the templated convenience function viskores::cont::make_ArrayHandleGroupVec() that takes an instance of the array to group into viskores::Vec’s.
You must specify the size of the viskores::Vec’s as a template parameter when using viskores::cont::make_ArrayHandleGroupVec().
-
template<viskores::IdComponent NUM_COMPONENTS, typename ArrayHandleType>
viskores::cont::ArrayHandleGroupVec<ArrayHandleType, NUM_COMPONENTS> viskores::cont::make_ArrayHandleGroupVec(const ArrayHandleType &array) make_ArrayHandleGroupVecis convenience function to generate an ArrayHandleGroupVec.It takes in an ArrayHandle and the number of components (as a specified template parameter), and returns an array handle with consecutive entries grouped in a Vec.
1 // Create an array containing [(0,1,2,3), (4,5,6,7), (8,9,10,11)]
2 viskores::cont::make_ArrayHandleGroupVec<4>(sourceArray)
Did You Know?
viskores::cont::ArrayHandleGroupVec requires you to specify the number of components at compile time.
For a similar functionality, consider using viskores::cont::ArrayHandleRuntimeVec, described in Section 4.8.4 (Runtime Vec Arrays).
This allows the runtime selection of viskores::Vec sizes and can be applied to existing basic arrays, but is limited in other ways.
viskores::cont::ArrayHandleGroupVec is handy when you need to build an array of vectors that are all of the same length, but what about when you need an array of vectors of different lengths?
One common use case for this is if you are defining a collection of polygons of different sizes (triangles, quadrilaterals, pentagons, and so on).
We would like to define an array such that the data for each polygon were stored in its own viskores::Vec (or, rather, Vec-like) object.
viskores::cont::ArrayHandleGroupVecVariable does just that.
viskores::cont::ArrayHandleGroupVecVariable takes two arrays. The first array, identified as the “source” array, is a flat representation of the values (much like the array used with viskores::cont::ArrayHandleGroupVec).
The second array, identified as the “offsets” array, provides for each vector the index into the source array where the start of the vector is.
The offsets array must be monotonically increasing.
The size of the offsets array is one greater than the number of vectors in the resulting array.
The first offset is always 0 and the last offset is always the size of the input source array.
The first and second template parameters to viskores::cont::ArrayHandleGroupVecVariable are the types for the source and offset arrays, respectively.
-
template<typename ComponentsArrayHandleType, typename OffsetsArrayHandleType>
class ArrayHandleGroupVecVariable : public viskores::cont::ArrayHandle<viskores::VecFromPortal<ComponentsArrayHandleType::WritePortalType>, viskores::cont::StorageTagGroupVecVariable<ComponentsArrayHandleType::StorageTag, OffsetsArrayHandleType::StorageTag>> Fancy array handle that groups values into vectors of different sizes.
It is sometimes the case that you need to run a worklet with an input or output that has a different number of values per instance. For example, the cells of a
viskores::cont::CellCetExplicitcan have different numbers of points in each cell. If inputting or outputting cells of this type, each instance of the worklet might need aviskores::Vecof a different length. This fancy array handle takes an array of values and an array of offsets and groups the consecutive values in Vec-like objects. The values are treated as tightly packed, so that each Vec contains the values from one offset to the next. The last value contains values from the last offset to the end of the array.For example, if you have an array handle with the 9 values 0,1,2,3,4,5,6,7,8 an offsets array handle with the 4 values 0,4,6,9 and give them to an
ArrayHandleGroupVecVariable, you get an array that looks like it contains three values of Vec-like objects with the data [0,1,2,3], [4,5], and [6,7,8].Note that caution should be used with
ArrayHandleRuntimeVecbecause the size of theviskores::Vecvalues is not known at compile time. Thus, the value type of this array is forced to a specialviskores::VecFromPortalclass that can cause surprises if treated as aviskores::Vec. In particular, the staticNUM_COMPONENTSexpression does not exist. Furthermore, new variables of typeviskores::VecFromPortalcannot be created. This means that simple operators like+will not work because they require an intermediate object to be created. (Equal operators like+=do work because they are given an existing variable to place the output.)The offsets array is often derived from a list of sizes for each of the entries. You can use the convenience function
viskores::cont::ConvertNumComponentsToOffsetsto take an array of sizes (i.e. the number of components for each entry) and get an array of offsets needed forArrayHandleGroupVecVariable.Public Functions
-
inline ArrayHandleGroupVecVariable(const ComponentsArrayHandleType &componentsArray, const OffsetsArrayHandleType &offsetsArray)
Construct an
ArrayHandleGroupVecVariable
-
inline ComponentsArrayHandleType GetComponentsArray() const
Return the components array providing the data for the grouped vec array.
-
inline OffsetsArrayHandleType GetOffsetsArray() const
Return the offsets array defining the locations and sizes of each value.
-
inline ArrayHandleGroupVecVariable(const ComponentsArrayHandleType &componentsArray, const OffsetsArrayHandleType &offsetsArray)
It is often the case that you will start with a group of vector lengths rather than offsets into the source array.
If this is the case, then the viskores::cont::ConvertNumComponentsToOffsets() helper function can convert an array of vector lengths to an array of offsets.
The first argument to this function is always the array of vector lengths.
The second argument, which is optional, is a reference to a viskores::cont::ArrayHandle into which the offsets should be stored.
If this offset array is not specified, an viskores::cont::ArrayHandle will be returned from the function instead.
The third argument, which is also optional, is a reference to a viskores::Id into which the expected size of the source array is put.
Having the size of the source array is often helpful, as it can be used to allocate data for the source array or check the source array’s size.
It is also OK to give the expected size reference but not the offset array reference.
-
void viskores::cont::ConvertNumComponentsToOffsets(const viskores::cont::UnknownArrayHandle &numComponentsArray, viskores::cont::ArrayHandle<viskores::Id> &offsetsArray, viskores::Id &componentsArraySize, viskores::cont::DeviceAdapterId device = viskores::cont::DeviceAdapterTagAny{})
ConvertNumComponentsToOffsetstakes an array of Vec sizes (i.e.the number of components in each
Vec) and returns an array of offsets to a packed array of suchVecs. The resulting array can be used withArrayHandleGroupVecVariable.Note that this function is pre-compiled for some set of
ArrayHandletypes. If you get a warning about an inefficient conversion (or the operation fails outright), you might need to useviskores::cont::internal::ConvertNumComponentsToOffsetsTemplate.- Parameters:
numComponentsArray – [in] the input array that specifies the number of components in each group Vec.
offsetsArray – [out] (optional) the output
ArrayHandle, which must have a value type ofviskores::Id. If the outputArrayHandleis not given, it is returned.componentsArraySize – [in] (optional) a reference to a
viskores::Idand is filled with the expected size of the component values array.device – [in] (optional) specifies the device on which to run the conversion.
1 // Create an array of counts containing [4, 2, 3, 3]
2 viskores::cont::ArrayHandle<viskores::IdComponent> countArray =
3 viskores::cont::make_ArrayHandle<viskores::IdComponent>({ 4, 2, 3, 3 });
4
5 // Convert the count array to an offset array [0, 4, 6, 9, 12]
6 // Returns the number of total components: 12
7 viskores::Id sourceArraySize;
8 using OffsetArrayType = viskores::cont::ArrayHandle<viskores::Id>;
9 OffsetArrayType offsetArray =
10 viskores::cont::ConvertNumComponentsToOffsets(countArray, sourceArraySize);
11
12 // Create an array containing [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
13 using SourceArrayType = viskores::cont::ArrayHandleIndex;
14 SourceArrayType sourceArray(sourceArraySize);
15
16 // Create an array containing [(0,1,2,3), (4,5), (6,7,8), (9,10,11)]
17 viskores::cont::ArrayHandleGroupVecVariable<SourceArrayType, OffsetArrayType>
18 vecVariableArray(sourceArray, offsetArray);
The viskores/cont/ArrayHandleGroupVecVariable.h header also contains the templated convenience function viskores::cont::make_ArrayHandleGroupVecVariable() that takes an instance of the source array to group into Vec-like objects and the offset array.
-
template<typename ComponentsArrayHandleType, typename OffsetsArrayHandleType>
viskores::cont::ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType> viskores::cont::make_ArrayHandleGroupVecVariable(const ComponentsArrayHandleType &componentsArray, const OffsetsArrayHandleType &offsetsArray) make_ArrayHandleGroupVecVariableis convenience function to generate an ArrayHandleGroupVecVariable.It takes in an ArrayHandle of values and an array handle of offsets and returns an array handle with consecutive entries grouped in a Vec.
1 // Create an array containing [(0,1,2,3), (4,5), (6,7,8), (9,10,11)]
2 viskores::cont::make_ArrayHandleGroupVecVariable(sourceArray, offsetArray)
Did You Know?
You can write to viskores::cont::ArrayHandleGroupVec and viskores::cont::ArrayHandleGroupVecVariable by, for example, using it as an output array.
Writes to these arrays will go to the respective location in the source array.
viskores::cont::ArrayHandleGroupVec can also be allocated and resized (which in turn causes the source array to be allocated).
However, viskores::cont::ArrayHandleGroupVecVariable cannot be resized and the source array must be pre-allocated.
You can use the source array size value returned from viskores::cont::ConvertNumComponentsToOffsets() to allocate source arrays.
Common Errors
Keep in mind that the values stored in a viskores::cont::ArrayHandleGroupVecVariable are not actually viskores::Vec objects.
Rather, they are “Vec-like” objects, which has some subtle but important ramifications.
First, the type will not match the viskores::Vec template, and there is no automatic conversion to viskores::Vec objects.
Thus, many functions that accept viskores::Vec objects as parameters will not accept the Vec-like object.
Second, the size of Vec-like objects are not known until runtime.
See Section 2.1.3 (Vector Types) and Section 4.1.6.2 (Vector Traits) for more information on the difference between viskores::Vec and Vec-like objects.
4.9.13. Random Arrays
The basis of generating random numbers in Viskores is built on the viskores::cont::ArrayHandleRandomUniformBits.
An uniform random bits array is a fancy array handle that generates pseudo random bits as viskores::Unit64 in its entries.
The uniform random bits array provides this array without actually using any memory.
-
class ArrayHandleRandomUniformBits : public viskores::cont::ArrayHandleImplicit<detail::PhiloxFunctor>
An
ArrayHandlethat provides a source of random bits.ArrayHandleRandomUniformBitsis a specialization ofArrayHandleImplicit. It takes a user supplied seed and hash it with the a given index value. The hashed value is the value of the array at that position.Currently, Philox2x32x10 as described in the “Parallel Random Numbers: As Easy as 1, 2, 3,” Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis (SC11) is used as the hash function.
Note: In contrast to traditional random number generator,
ArrayHandleRandomUniformBitsdoes not have “state”, i.e. multiple calls the Get() method with the same index will always return the same hash value. To get a new set of random bits, create a newArrayHandleRandomUniformBitswith a different seed.Public Functions
-
inline explicit ArrayHandleRandomUniformBits(viskores::Id length, SeedType seed = {std::random_device{}()})
Construct an
ArrayHandleRandomUniformBits.- Parameters:
length – Specifies the length of the generated array.
seed – Provides a seed to use for the pseudorandom numbers. To prevent confusion between the seed and the length, the type of the seed is a
viskores::Vecof size 1. To specify the seed, declare it in braces. For example, to construct a random array of size 50 with seed 123, useArrayHandleRandomUniformBits(50, { 123 }).
-
inline explicit ArrayHandleRandomUniformBits(viskores::Id length, SeedType seed = {std::random_device{}()})
The constructor for viskores::cont::ArrayHandleRandomUniformBits takes two arguments: the first argument is the length of the array handle, the second is a seed of type viskores::Vec<Uint32, 1>.
If the seed is not specified, the C++11 std::random_device is used as default.
1 // Create an array containing a sequence of random bits seeded
2 // by std::random_device.
3 viskores::cont::ArrayHandleRandomUniformBits randomArray(50);
4 // Create an array containing a sequence of random bits with
5 // a user supplied seed.
6 viskores::cont::ArrayHandleRandomUniformBits randomArraySeeded(50, { 123 });
viskores::cont::ArrayHandleRandomUniformBits is functional, in the sense that once an instance of viskores::cont::ArrayHandleRandomUniformBits is created, its content does not change and always returns the same viskores::UInt64 value given the same index.
1 // ArrayHandleRandomUniformBits is functional, it returns
2 // the same value for the same entry is accessed.
3 auto r0 = randomArray.ReadPortal().Get(5);
4 auto r1 = randomArray.ReadPortal().Get(5);
5 assert(r0 == r1);
To generate a new set of random bits, we need to create another instance of viskores::cont::ArrayHandleRandomUniformBits with a different seed, we can either let std::random_device provide a unique seed or use some unique identifier such as iteration number as the seed.
1 // Create a new insance of ArrayHandleRandomUniformBits
2 // for each set of random bits.
3 viskores::cont::ArrayHandleRandomUniformBits randomArray0(50, { 0 });
4 viskores::cont::ArrayHandleRandomUniformBits randomArray1(50, { 1 });
5 assert(randomArray0.ReadPortal().Get(5) != randomArray1.ReadPortal().Get(5));
The random bits provided by viskores::cont::ArrayHandleRandomUniformBits can be manipulated to provide random numbers with specific distributions.
Viskores provides some specialized classes that implement common distributions.
The viskores::cont::ArrayHandleRandomUniformReal class generates an array of numbers sampled from a real uniform distribution in the range \([0, 1)\).
-
template<typename Real = viskores::Float64>
class ArrayHandleRandomUniformReal : public viskores::cont::ArrayHandleTransform<viskores::cont::ArrayHandleRandomUniformBits, detail::CanonicalFunctor<viskores::Float64>> An
ArrayHandlethat provides a source of random numbers with uniform distribution.ArrayHandleRandomUniformRealtakes a user supplied seed and hashes it to provide a sequence of numbers drawn from a random uniform distribution in the range [0, 1).ArrayHandleRandomUniformRealis built on top ofArrayHandleRandomUniformBitsso shares its behavior with that array.Note: In contrast to traditional random number generator,
ArrayHandleRandomUniformRealdoes not have “state”, i.e. multiple calls the Get() method with the same index will always return the same hash value. To get a new set of random bits, create a newArrayHandleRandomUniformBitswith a different seed.Public Functions
-
inline explicit ArrayHandleRandomUniformReal(viskores::Id length, SeedType seed = {std::random_device{}()})
Construct an
ArrayHandleRandomUniformReal.- Parameters:
length – Specifies the length of the generated array.
seed – Provides a seed to use for the pseudorandom numbers. To prevent confusion between the seed and the length, the type of the seed is a
viskores::Vecof size 1. To specify the seed, declare it in braces. For example, to construct a random array of size 50 with seed 123, useArrayHandleRandomUniformReal(50, { 123 }).
-
inline explicit ArrayHandleRandomUniformReal(viskores::Id length, SeedType seed = {std::random_device{}()})
1 constexpr viskores::Id NumPoints = 50;
2 auto randomPointsInBox = viskores::cont::make_ArrayHandleCompositeVector(
3 viskores::cont::ArrayHandleRandomUniformReal<viskores::FloatDefault>(NumPoints),
4 viskores::cont::ArrayHandleRandomUniformReal<viskores::FloatDefault>(NumPoints),
5 viskores::cont::ArrayHandleRandomUniformReal<viskores::FloatDefault>(NumPoints));
The viskores::cont::ArrayHandleRandomStandardNormal class generates an array of numbers sampled from a standard normal distribution.
This provides a set of points centered at 0 and with probability exponentially diminishing away from 0 in both the positive and negative directions.
-
template<typename Real = viskores::Float64>
class ArrayHandleRandomStandardNormal : public viskores::cont::ArrayHandleTransform<viskores::cont::ArrayHandleZip<viskores::cont::ArrayHandleRandomUniformReal<viskores::Float64>, viskores::cont::ArrayHandleRandomUniformReal<viskores::Float64>>, detail::BoxMuller> An
ArrayHandlethat provides a source of random numbers with a standard normal distribution.ArrayHandleRandomStandardNormaltakes a user supplied seed and hashes it to provide a sequence of numbers drawn from a random standard normal distribution. The probability density function of the numbers is \(\frac{e^{-x^2/2}}{\sqrt{2\pi}}\). The range of possible values is technically infinite, but the probability of large positive or negative numbers becomes vanishingly small.This array uses the Box-Muller transform to pick random numbers in the stanard normal distribution.
Note: In contrast to traditional random number generator,
ArrayHandleRandomStandardNormaldoes not have “state”, i.e. multiple calls the Get() method with the same index will always return the same hash value. To get a new set of random bits, create a newArrayHandleRandomUniformBitswith a different seed.Public Functions
-
inline explicit ArrayHandleRandomStandardNormal(viskores::Id length, SeedType seed = {std::random_device{}()})
Construct an
ArrayHandleRandomStandardNormal.- Parameters:
length – Specifies the length of the generated array.
seed – Provides a seed to use for the pseudorandom numbers. To prevent confusion between the seed and the length, the type of the seed is a
viskores::Vecof size 1. To specify the seed, declare it in braces. For example, to construct a random array of size 50 with seed 123, useArrayHandleRandomStandardNormal(50, { 123 }).
-
inline explicit ArrayHandleRandomStandardNormal(viskores::Id length, SeedType seed = {std::random_device{}()})
1 constexpr viskores::Id NumPoints = 50;
2 auto randomPointsInGaussian = viskores::cont::make_ArrayHandleCompositeVector(
3 viskores::cont::ArrayHandleRandomStandardNormal<viskores::FloatDefault>(NumPoints),
4 viskores::cont::ArrayHandleRandomStandardNormal<viskores::FloatDefault>(NumPoints),
5 viskores::cont::ArrayHandleRandomStandardNormal<viskores::FloatDefault>(
6 NumPoints));
Did You Know?
The distributions of the provided random array handles can manipulated by shifting and scaling the values they provide.
This will keep the general distribution shape but change the range.
This manipulation can happen in a worklet from the values returned from the arrays or they can be generated automatically by wrapping the random arrays in a viskores::cont::ArrayHandleTransform.