-
Notifications
You must be signed in to change notification settings - Fork 0
/
FancyArrayStorage.tex
1117 lines (898 loc) · 55.1 KB
/
FancyArrayStorage.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% -*- latex -*-
\chapter{Fancy Array Storage}
\label{chap:Storage}
\index{array handle|(}
Chapter~\ref{chap:ArrayHandle} introduces the \vtkmcont{ArrayHandle} class.
In it, we learned how an \textidentifier{ArrayHandle} manages the
memory allocation of an array, provides access to the data via array
portals, and supervises the movement of data between the control and
execution environments.
\index{array handle!storage|(}
\index{storage|(}
In addition to these data management features, \textidentifier{ArrayHandle}
also provides a configurable \keyterm{storage} mechanism that allows you,
through efficient template configuration, to redefine how data are stored
and retrieved. The storage object provides an encapsulated interface around
the data so that any necessary strides, offsets, or other access patterns
may be handled internally. The relationship between array handles and their
storage object is shown in Figure~\ref{fig:ArrayHandleStorage}.
\begin{figure}[htb]
\centering
\includegraphics{images/ArrayHandleStorage}
\caption{Array handles, storage objects, and the underlying data source.}
\label{fig:ArrayHandleStorage}
\end{figure}
One interesting consequence of using a generic storage object to manage
data within an array handle is that the storage can be defined
functionally rather than point to data stored in physical memory. Thus,
implicit array handles are easily created by adapting to functional
``storage.'' For example, the point coordinates of a uniform rectilinear
grid are implicit based on the topological position of the point. Thus, the
point coordinates for uniform rectilinear grids can be implemented as an
implicit array with the same interface as explicit arrays (where
unstructured grid points would be stored). In this chapter we explore the
many ways you can manipulate the \textidentifier{ArrayHandle} storage.
\begin{didyouknow}
VTK-m comes with many ``fancy'' array handles that can change the data in
other arrays without modifying the memory or can generate data on the fly
to behave like an array without actually using any memory. These fancy
array handles are documented later in this chapter, and they can be very
handy when developing with VTK-m.
\end{didyouknow}
\section{Basic Storage}
\index{array handle!storage!default}
\index{storage!default}
As previously discussed in Chapter~\ref{chap:ArrayHandle},
\vtkmcont{ArrayHandle} takes two template arguments.
\begin{vtkmexample}{Declaration of the \protect\vtkmcont{ArrayHandle} templated class (again).}
template<
typename T,
typename StorageTag = VTKM_DEFAULT_STORAGE_TAG>
class ArrayHandle;
\end{vtkmexample}
The first argument is the only one required and has been demonstrated
multiple times before. The second (optional) argument specifies something
called a storage, which provides the interface between the generic
\vtkmcont{ArrayHandle} class and a specific storage mechanism in the
control environment.
In this and the following sections we describe this storage mechanism. A
default storage is specified in much the same way as a default device
adapter is defined (as described in Section~\ref{sec:DefaultDeviceAdapter}.
It is done by setting the \vtkmmacro{VTKM\_STORAGE} macro. This macro must
be set before including any VTK-m header files. Currently the only
practical storage provided by VTK-m is the basic storage, which simply
allocates a continuous section of memory of the given base type. This
storage can be explicitly specified by setting \vtkmmacro{VTKM\_STORAGE} to
\vtkmmacro{VTKM\_STORAGE\_BASIC} although the basic storage will also be
used as the default if no other storage is specified (which is typical).
The default storage can always be overridden by specifying an array
storage tag. The tag for the basic storage is located in the
\vtkmheader{vtkm/cont}{StorageBasic.h} header file and is named
\vtkmcont{StorageTagBasic}. Here is an example of specifying
the storage type when declaring an array handle.
\vtkmlisting{Specifying the storage type for an \textidentifier{ArrayHandle.}}{ArrayHandleStorageParameter.cxx}
VTK-m also defines a macro named \vtkmmacro{VTKM\_DEFAULT\_STORAGE\_TAG}
that can be used in place of an explicit storage tag to use the
default tag. This macro is used to create new templates that have template
parameters for storage that can use the default.
\index{array handle!fancy|(}
\index{fancy array handle|(}
\section{Provided Fancy Arrays}
\label{sec:ProvidedFancyArrays}
The generic array handle and storage templating in VTK-m allows for
any type of operations to retrieve a particular value. Typically this is
used to convert an index to some location or locations in memory. However,
it is also possible to do many other operations. 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. This modified behavior for arrays is called ``fancy'' arrays.
VTK-m provides many of the fancy arrays, which we explore in this section.
Later Section~\ref{sec:ImplementingFancyArrays} describes many different
ways in which new fancy arrays can be implemented.
\subsection{Constant Arrays}
\label{sec:ConstantArrays}
\index{array handle!constant|(}
\index{constant array handle|(}
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 VTK-m is straightforward. VTK-m has a class
named \vtkmcont{ArrayHandleConstant}. \textidentifier{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
\textidentifier{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.
\vtkmlisting{Using \textidentifier{ArrayHandleConstant}.}{ArrayHandleConstant.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandleConstant.h} header also contains the
templated convenience function \vtkmcont{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.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleConstant}.}{MakeArrayHandleConstant.cxx}
\index{constant array handle|)}
\index{array handle!constant|)}
\subsection{Counting Arrays}
\label{sec:CountingArrays}
\index{array handle!counting|(}
\index{counting array handle|(}
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.
\index{array handle!index|(}
\index{index array handle|(}
VTK-m 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 \vtkm{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.
Specifying an index array in VTK-m is done with a class named
\vtkmcont{ArrayHandleIndex}. The constructor for
\textidentifier{ArrayHandleIndex} takes the size of the array to create.
The following example is a simple demonstration of the index array handle.
\vtkmlisting{Using \textidentifier{ArrayHandleIndex}.}{ArrayHandleIndex.cxx}
\index{index array handle|)}
\index{array handle!index|)}
The \vtkmcont{ArrayHandleCounting} class provides a more general form of
counting. \textidentifier{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 \textidentifier{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.
\vtkmlisting{Using \textidentifier{ArrayHandleCounting}.}{ArrayHandleCountingBasic.cxx}
\begin{didyouknow}
In addition to being simpler to declare,
\textidentifier{ArrayHandleIndex} is slightly faster than
\textidentifier{ArrayHandleCounting}. Thus, when applicable, you should
prefer using \textidentifier{ArrayHandleIndex}.
\end{didyouknow}
The \vtkmheader{vtkm/cont}{ArrayHandleCounting.h} header also contains the
templated convenience function \vtkmcont{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.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleCounting}.}{MakeArrayHandleCountingBasic.cxx}
There are no fundamental limits on how \textidentifier{ArrayHandleCounting}
counts. For example, it is possible to count backwards.
\vtkmlisting{Counting backwards with \textidentifier{ArrayHandleCounting}.}{ArrayHandleCountingBackward.cxx}
It is also possible to use \textidentifier{ArrayHandleCounting} to make
sequences of \vtkm{Vec} values with piece-wise counting in each of the
components.
\vtkmlisting{Using \textidentifier{ArrayHandleCounting} with \protect\vtkm{Vec} objects.}{ArrayHandleCountingVec.cxx}
\index{counting array handle|)}
\index{array handle!counting|)}
\subsection{Cast Arrays}
\label{sec:CastArrays}
\index{array handle!cast|(}
\index{cast array handle|(}
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.
VTK-m has a class named \vtkmcont{ArrayHandleCast} to perform this implicit
casting. \textidentifier{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 \textidentifier{ArrayHandle}.
The constructor to \textidentifier{ArrayHandleCast} takes the
\textidentifier{ArrayHandle} to modify by casting.
\vtkmlisting{Using \textidentifier{ArrayHandleCast}.}{ArrayHandleCast.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandleCast.h} header also contains the
templated convenience function \vtkmcont{make\_ArrayHandleCast} that
constructs the cast array. The first argument is the original
\textidentifier{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.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleCast}.}{MakeArrayHandleCast.cxx}
\index{cast array handle|)}
\index{array handle!cast|)}
\subsection{Discard Arrays}
\label{sec:DiscardArrays}
\index{discard array handle|(}
\index{array handle!discard|(}
It is sometimes the case where you will want to run an operation in \VTKm 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, \VTKm provides \vtkmcont{ArrayHandleDiscard}.
This array behaves similar to a regular \textidentifier{ArrayHandle} in that it can be ``allocated'' and has size, but any values that are written to it are immediately discarded.
\textidentifier{ArrayHandleDiscard} takes up no memory.
\vtkmlisting{Using \textidentifier{ArrayHandleDiscard}.}{ArrayHandleDiscard.cxx}
\index{array handle!discard|)}
\index{discard array handle|)}
\subsection{Permuted Arrays}
\label{sec:PermutedArrays}
\index{array handle!permutation|(}
\index{permuted array handle|(}
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 VTK-m is straightforward. VTK-m has a
class named \vtkmcont{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.
\vtkmlisting{Using \textidentifier{ArrayHandlePermutation}.}{ArrayHandlePermutation.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandlePermutation.h} header also contains the
templated convenience function \vtkmcont{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.
\vtkmlisting{Using \textidentifier{make\_ArrayHandlePermutation}.}{MakeArrayHandlePermutation.cxx}
\begin{commonerrors}
When using an \textidentifier{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.
\end{commonerrors}
\begin{didyouknow}
You can write to a \textidentifier{ArrayHandlePermutation} by, for
example, using it as an output array. Writes to the
\textidentifier{ArrayHandlePermutation} will go to the respective
location in the source array. However,
\textidentifier{ArrayHandlePermutation} cannot be resized.
\end{didyouknow}
\index{permuted array handle|)}
\index{array handle!permutation|)}
\subsection{Zipped Arrays}
\label{sec:ZippedArrays}
\index{array handle!zip|(}
\index{zipped array handles|(}
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 \vtkm{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 VTK-m is straightforward. VTK-m has a class
named \vtkmcont{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.
\vtkmlisting{Using \textidentifier{ArrayHandleZip}.}{ArrayHandleZip.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandleZip.h} header also contains the
templated convenience function \vtkmcont{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.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleZip}.}{MakeArrayHandleZip.cxx}
\index{zipped array handles|)}
\index{array handle!zip|)}
\subsection{Coordinate System Arrays}
\label{sec:CoordinateSystemArrays}
Many of the data structures we use in VTK-m are described in a 3D
coordinate system. Although, as we will see in Chapter~\ref{chap:DataSets},
we can use any \textidentifier{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.
\index{array handle!uniform point coordinates|(}
\index{uniform point coordinates array handle|(}
There are two fancy array handles that each handle a special form of
coordinate system. The first such array handle is
\vtkmcont{ArrayHandleUniformPointCoordinates}, which represents a uniform
sampling of space. The constructor for
\textidentifier{ArrayHandleUniformPointCoordinates} takes three arguments.
The first argument is a \vtkm{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.
\vtkmlisting{Using \textidentifier{ArrayHandleUniformPointCoordinates}.}{ArrayHandleUniformPointCoordinates.cxx}
\index{uniform point coordinates array handle|)}
\index{array handle!uniform point coordinates|)}
\index{array handle!Cartesian product|(}
\index{array handle!rectilinear point coordinates|(}
\index{Cartesian product array handle|(}
\index{rectilinear point coordinates array handle|(}
The second fancy array handle for special coordinate systems is
\vtkmcont{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.
\textidentifier{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
\textidentifier{ArrayHandleCartesianProduct} takes the three arrays.
\vtkmlisting{Using a \textidentifier{ArrayHandleCartesianProduct}.}{ArrayHandleCartesianProduct.cxx}
The \vtkmheader{vtkm/cont/ArrayHandleCartesianProduct.h} header also
contains the templated convenience function
\vtkmcont{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.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleCartesianProduct}.}{MakeArrayHandleCartesianProduct.cxx}
\index{rectilinear point coordinates array handle|)}
\index{Cartesian product array handle|)}
\index{array handle!rectilinear point coordinates|)}
\index{array handle!Cartesian product|)}
\begin{didyouknow}
These specialized arrays for coordinate systems greatly reduce the code
duplication in VTK-m. Most scientific visualization systems need separate
implementations of algorithms for uniform, rectilinear, and unstructured
grids. But in VTK-m 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.
\end{didyouknow}
\subsection{Composite Vector Arrays}
\label{sec:CompositeVectorArrays}
\index{array handle!composite vector arrays|(}
\index{composite vector arrays array handle|(}
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 \vtkm{Vec}. A composite vector array is similar in nature
to a zipped array (described in Section~\ref{sec:ZippedArrays}) except that
values are combined into \vtkm{Vec}s instead of \vtkm{Pair}s. The created
\vtkm{Vec}s are not stored in their own memory space. Rather, the
\textidentifier{Vec}s are generated as the array is used. Writing
\textidentifier{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
\vtkmcont{ArrayHandleCompositeVector} class. This class has a single
template argument that is a ``signature'' for the arrays to be combined.
These signatures can be tricky to prototype, so
\vtkmheader{vtkm/cont/ArrayHandleCompositeVector.h} header also contains a
helper struct named \vtkmcont{ArrayHandleCompositeVectorType} to define the
type. \textidentifier{ArrayHandleCompositeVectorType} takes a variable
number of \textidentifier{ArrayHandle} types that compose the vector.
\textidentifier{ArrayHandleCompositeVectorType} has an internal type named
\textcode{type} that is the appropriately defined
\textidentifier{ArrayHandleCompositeVector}.
The constructor for \textidentifier{ArrayHandleCompositeVector} takes
instances of the array handles to combine along with the component from
each array to use. If the array handles being combined contain scalar data,
then the appropriate component to use is 0.
\vtkmlisting{Using \textidentifier{ArrayHandleCompositeVector}.}{ArrayHandleCompositeVectorBasic.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandleCompositeVector.h} header also
contains the templated convenience function
\vtkmcont{make\_ArrayHandleCompositeVector} which takes two to four array
handles and returns an \textidentifier{ArrayHandleCompositeVector}. This
function can sometimes be used to avoid having to declare the full array
type.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleCompositeVector}.}{MakeArrayHandleCompositeVector.cxx}
\textidentifier{ArrayHandleCompositeVector} is often used to combine scalar
arrays into vector arrays, but it can also be used to pull components out
of other vector arrays. The following example uses this feature to convert
an array of 2D $x,y$ coordinates and an array of elevations to 3D $x,y,z$
coordinates.
\vtkmlisting{Combining vector components with \textidentifier{ArrayHandleCompositeVector}.}{ArrayHandleCompositeVectorComponents.cxx}
\index{composite vector arrays array handle|)}
\index{array handle!composite vector arrays|)}
\subsection{Grouped Vector Arrays}
\label{sec:GroupedVectorArrays}
\index{array handle!group vector|(}
\index{group vector array handle|(}
A grouped vector array is a fancy array handle that groups consecutive
values of an array together to form a \vtkm{Vec}. The source array must be
of a length that is divisible by the requested \textidentifier{Vec} size.
The created \vtkm{Vec}s are not stored in their own memory space. Rather,
the \textidentifier{Vec}s are generated as the array is used. Writing
\textidentifier{Vec}s to the grouped vector array writes values into the
the source array.
A grouped vector array is created using the \vtkmcont{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 \textidentifier{Vec}s to create (the number of
values to group together).
\vtkmlisting{Using \textidentifier{ArrayHandleGroupVec}.}{ArrayHandleGroupVecBasic.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandleGroupVec.h} header also contains the
templated convenience function \vtkmcont{make\_ArrayHandleGroupVec} that
takes an instance of the array to group into \textidentifier{Vec}s. You
must specify the size of the \textidentifier{Vec}s as a template parameter
when using \vtkmcont{make\_ArrayHandleGroupVec}.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleGroupVec}.}{MakeArrayHandleGroupVec.cxx}
\textidentifier{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 \textidentifier{Vec} (or, rather, \Veclike) object.
\vtkmcont{ArrayHandleGroupVecVariable} does just that.
\textidentifier{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 \textidentifier{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 first and second template parameters to \textidentifier{ArrayHandleGroupVecVariable} are the types for the source and offset arrays, respectively.
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 \vtkmcont{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 \textidentifier{ArrayHandle} into which the offsets should be stored.
If this offset array is not specified, an \textidentifier{ArrayHandle} will be returned from the function instead.
The third argument, which is also optional, is a reference to a \vtkm{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.
\vtkmlisting{Using \textidentifier{ArrayHandleGroupVecVariable}.}{ArrayHandleGroupVecVariable.cxx}
The \vtkmheader{vtkm/cont}{ArrayHandleGroupVecVariable.h} header also contains the templated convenience function \vtkmcont{make\_ArrayHandleGroupVecVariable} that takes an instance of the source array to group into \Veclike objects and the offset array.
\vtkmlisting{Using \textidentifier{MakeArrayHandleGroupVecVariable}.}{MakeArrayHandleGroupVecVariable.cxx}
\begin{didyouknow}
You can write to \textidentifier{ArrayHandleGroupVec} and \textidentifier{ArrayHandleGroupVecVariable} by, for example, using it as an output array.
Writes to these arrays will go to the respective location in the source array.
\textidentifier{ArrayHandleGroupVec} can also be allocated and resized (which in turn causes the source array to be allocated).
However, \textidentifier{ArrayHandleGroupVecVariable} cannot be resized and the source array must be pre-allocated.
You can use the source array size value returned from \textidentifier{ConvertNumComponentsToOffsets} to allocate source arrays.
\end{didyouknow}
\begin{commonerrors}
Keep in mind that the values stored in a \textidentifier{ArrayHandleGroupVecVariable} are not actually \vtkm{Vec} objects.
Rather, they are ``\Veclike'' objects, which has some subtle but important ramifications.
First, the type will not match the \vtkm{Vec} template, and there is no automatic conversion to \vtkm{Vec} objects.
Thus, many functions that accept \vtkm{Vec} objects as parameters will not accept the \Veclike object.
Second, the size of \Veclike objects are not known until runtime.
See Sections \ref{sec:VectorTypes} and \ref{sec:VectorTraits} for more information on the difference between \vtkm{Vec} and \Veclike objects.
\end{commonerrors}
\index{group vector array handle|)}
\index{array handle!group vector|)}
\section{Implementing Fancy Arrays}
\label{sec:ImplementingFancyArrays}
Although the behavior of fancy arrays might seem complicated, they are
actually straightforward to implement. VTK-m provides several mechanisms to
implement fancy arrays.
\subsection{Implicit Array Handles}
\index{array handle!implicit|(}
\index{storage!implicit|(}
\index{implicit storage|(}
\index{implicit array handle|(}
\index{functional array|(}
The generic array handle and storage templating in VTK-m allows for
any type of operations to retrieve a particular value. Typically this is
used to convert an index to some location or locations in memory. However,
it is also possible to compute a value directly from an index rather than
look up some value in memory. Such an array is completely functional and
requires no storage in memory at all. Such a functional array is called an
\keyterm{implicit array handle}. Implicit arrays are an example of
\keyterm{fancy array handles}, which are array handles that behave like
regular arrays but do special processing under the covers to provide
values.
Specifying a functional or implicit array in VTK-m is straightforward.
VTK-m has a special class named \vtkmcont{ArrayHandleImplicit} that makes
an implicit array containing values generated by a user-specified
\keyterm{functor}. \index{functor} A functor is simply a C++ class or
struct that contains an overloaded parenthesis operator so that it can be
used syntactically like a function.
To demonstrate the use of \textidentifier{ArrayHandleImplicit}, let us say
we want an array of even numbers. The array has the values
$[0,2,4,6,\ldots]$ (double the index) up to some given size. Although we
could easily create this array in memory, we can save space and possibly
time by computing these values on demand.
\begin{didyouknow}
VTK-m already comes with an implicit array handle named
\vtkmcont{ArrayHandleCounting} that can make implicit even numbers as
well as other more general counts. So in practice you would not have to
create a special implicit array, but we are doing so here for
demonstrative purposes.
\end{didyouknow}
The first step to using \textidentifier{ArrayHandleImplicit} is to declare
a functor. The functor's parenthesis operator should accept a single
argument of type \vtkm{Id} and return a value appropriate for that index.
The parenthesis operator should also be declared \textcode{const} because
it is not allowed to change the class' state.
\vtkmlisting{Functor that doubles an index.}{ImplicitArrayFunctor.cxx}
Once the functor is defined, an implicit array can be declared using the
templated \vtkmcont{ArrayHandleImplicit} class. The first template argument
is the type of the array's values (which should match the return value for
the functor), and the second template argument is the functor type.
\vtkmlisting{Declaring a \textidentifier{ArrayHandleImplicit}.}{DeclareImplicitArray.cxx}
For convenience, \vtkmheader{vtkm/cont}{ArrayHandleImplicit.h} also
declares the \vtkmcont{make\_ArrayHandleImplicit} function. This function
takes a functor and the size of the array and returns the implicit array.
When using this function, you also have to declare the first template
argument, which is the array's value type, since this type does not appear
in any of the arguments.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleImplicit}.}{MakeArrayHandleImplicit.cxx}
\index{array handle!subclassing}
If the implicit array you are creating tends to be generally useful and is
something you use multiple times, it might be worthwhile to make a
convenience subclass of \vtkmcont{ArrayHandleImplicit} for your array.
\vtkmlisting[ex:ImplicitArrayHandleSubclass]{Custom implicit array handle for even numbers.}{ImplicitArrayHandle2.cxx}
Subclasses of \textidentifier{ArrayHandle} provide constructors that
establish the state of the array handle. All array handle subclasses must
also use either the \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS} macro or the
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} macro. Both of these macros
define the typedefs \textcode{Superclass}, \textcode{ValueType}, and
\textcode{StorageTag} as well as a set of constructors and operators
expected of all \textidentifier{ArrayHandle} classes. The difference
between these two macros is that \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS}
is used in templated classes whereas
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} is used in non-templated
classes.
The \textidentifier{ArrayHandle} subclass in
Example~\ref{ex:ImplicitArrayHandleSubclass} is not templated, so it uses
the \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} macro. (The other macro
is described in Section~\ref{sec:VTKM_ARRAY_HANDLE_SUBCLASS} on
page~\pageref{sec:VTKM_ARRAY_HANDLE_SUBCLASS}). This macro takes two
parameters. The first parameter is the name of the subclass where the macro
is defined and the second parameter is the immediate superclass including
the full template specification. The second parameter of the macro must be
enclosed in parentheses so that the C pre-processor correctly handles
commas in the template specification.
\index{functional array|)}
\index{implicit array handle|)}
\index{implicit storage|)}
\index{storage!implicit|)}
\index{array handle!implicit|)}
\subsection{Transformed Arrays}
\label{sec:TransformedArrays}
\index{array handle!transform|(}
\index{transformed array|(}
Another type of fancy array handle is the transformed array. A transformed
array takes another array and applies a function to all of the elements to
produce a new array. A transformed array behaves much like a map operation
except that a map operation writes its values to a new memory location
whereas the transformed array handle produces its values on demand so that
no additional storage is required.
Specifying a transformed array in VTK-m is straightforward. VTK-m has a
special class named \vtkmcont{ArrayHandleTransform} that takes an array
handle and a functor and provides an interface to a new array comprising
values of the first array applied to the functor.
To demonstrate the use of \textidentifier{ArrayHandleTransform}, let us say
that we want to scale and bias all of the values in a target array. That
is, each value in the target array is going to be multiplied by a given
scale and then offset by adding a bias value. (The scale and bias are
uniform across all entries.) We could, of course, easily create a worklet
to apply this scale and bias to each entry in the target array and save the
result in a new array, but we can save space and possibly time by computing
these values on demand.
The first step to using \textidentifier{ArrayHandleTransform} is to declare
a functor. The functor's parenthesis operator should accept a single
argument of the type of the target array and return the transformed value.
For more generally applicable transform functors, it is often useful to
make the parenthesis operator a template. The parenthesis operator should
also be declared \textcode{const} because it is not allowed to change the
class' state.
\vtkmlisting{Functor to scale and bias a value.}{TransformArrayFunctor.cxx}
Once the functor is defined, a transformed array can be declared using the
templated \vtkmcont{ArrayHandleTransform} class. The first template
argument is the type of the array's values (which should match the return
value for the functor). The second template argument is the type of array
being transformed. The third and final template argument is the type of
functor used for the transformation.
That said, it is generally easier to use the
\vtkmcont{make\_ArrayHandleTransform} convenience function. This function
takes an array and a functor and returns a transformed array. When using
this function, you also have to declare the first template argument, which
is the transformed array's value type, since this type does not appear in
any of the arguments.
\vtkmlisting{Using \textidentifier{make\_ArrayHandleTransform}.}{MakeArrayHandleTransform.cxx}
If the transformed array you are creating tends to be generally useful and
is something you use multiple times, it might be worthwhile to make a
convenience subclass of \vtkmcont{ArrayHandleTransform} or convenience
\textcode{make\_ArrayHandle*} function for your array.
\vtkmlisting[ex:TransformArrayHandleSubclass]{Custom transform array handle for scale and bias.}{TransformArrayHandle.cxx}
\label{sec:VTKM_ARRAY_HANDLE_SUBCLASS}
Subclasses of \textidentifier{ArrayHandle} provide constructors that
establish the state of the array handle. All array handle subclasses must
also use either the \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS} macro or the
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} macro. Both of these macros
define the typedefs \textcode{Superclass}, \textcode{ValueType}, and
\textcode{StorageTag} as well as a set of constructors and operators
expected of all \textidentifier{ArrayHandle} classes. The difference
between these two macros is that \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS}
is used in templated classes whereas
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} is used in non-templated
classes.
The \textidentifier{ArrayHandle} subclass in
Example~\ref{ex:TransformArrayHandleSubclass} is templated, so it uses the
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS} macro. (The other macro is
described in Section~\ref{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT} on
page~\pageref{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT}). This macro takes three
parameters. The first parameter is the name of the subclass where the macro
is defined, the second parameter is the type of the subclass including the
full template specification, and the third parameter is the immediate
superclass including the full template specification. The second and third
parameters of the macro must be enclosed in parentheses so that the C
pre-processor correctly handles commas in the template specification.
\index{transformed array|)}
\index{array handle!transform|)}
\subsection{Derived Storage}
\label{sec:DerivedStorage}
\index{array handle!derived|(}
\index{storage!derived|(}
\index{derived storage|(}
A \keyterm{derived storage} is a type of fancy array that takes one or more
other arrays and changes their behavior in some way. A transformed array
(Section~\ref{sec:TransformedArrays}) is a specific type of derived array
with a simple mapping. In this section we will demonstrate the steps
required to create a more general derived storage. When applicable, it is
much easier to create a derived array as a transformed array or using the
other fancy arrays than to create your own derived storage. However, if
these pre-existing fancy arrays do not work work, for example if your
derivation uses multiple arrays or requires general lookups, you can do so
by creating your own derived storage. For the purposes of the example in
this section, let us say we want 2 array handles to behave as one array
with the contents concatenated together. We could of course actually copy
the data, but we can also do it in place.
The first step to creating a derived storage is to build an array portal
that will take portals from arrays being derived. The portal must work in
both the control and execution environment (or have a separate version for
control and execution).
\vtkmlisting[ex:DerivedArrayPortal]{Derived array portal for concatenated arrays.}{DerivedArrayPortal.cxx}
Like in an adapter storage, the next step in creating a derived storage
is to define a tag for the adapter. We shall call ours
\textcode{StorageTagConcatenate} and it will be templated on
the two array handle types that we are deriving. Then, we need to create a
specialization of the templated \vtkmcontinternal{Storage}
class. The implementation for a \textidentifier{Storage} for
a derived storage is usually trivial compared to an adapter storage
because the majority of the work is deferred to the derived arrays.
\vtkmlisting[ex:DerivedArrayStorage]{\textidentifier{Storage} for derived container of concatenated arrays.}{DerivedArrayStorage.cxx}
One of the responsibilities of an array handle is to copy data between the
control and execution environments. The default behavior is to request the
device adapter to copy data items from one environment to another. This
might involve transferring data between a host and device. For an array of
data resting in memory, this is necessary. However, implicit storage
(described in the previous section) overrides this behavior to pass nothing
but the functional array portal. Likewise, it is undesirable to do a raw
transfer of data with derived storage. The underlying arrays being
derived may be used in other contexts, and it would be good to share the
data wherever possible. It is also sometimes more efficient to copy data
independently from the arrays being derived than from the derived storage
itself.
\index{array transfer|(}
The mechanism that controls how a particular storage gets
transferred to and from the execution environment is encapsulated in the
templated \vtkmcontinternal{ArrayTransfer} class. By creating a
specialization of \vtkmcontinternal{ArrayTransfer}, we can modify the
transfer behavior to instead transfer the arrays being derived and use the
respective copies in the control and execution environments.
\vtkmcontinternal{ArrayTransfer} has three template arguments: the base type
of the array, the storage tag, and the device adapter tag.
\vtkmlisting[ex:ArrayTransferPrototype]{Prototype for \protect\vtkmcontinternal{ArrayTransfer}.}{ArrayTransferPrototype.cxx}
All \vtkmcontinternal{ArrayTransfer} implementations must have a
constructor method that accepts a pointer to a \vtkmcontinternal{Storage}
object templated to the same base type and storage tag as the
\textidentifier{ArrayTransfer} object. Assuming that an
\textidentifier{ArrayHandle} is templated using the parameters in
Example~\ref{ex:ArrayTransferPrototype}, the prototype for the constructor
must be equivalent to the following.
\begin{vtkmexample}{Prototype for \textidentifier{ArrayTransfer} constructor.}
ArrayTransfer(vtkm::cont::internal::Storage<T, StorageTag> *storage);
\end{vtkmexample}
Typically the constructor either saves the \textidentifier{Storage} pointer
or other relevant objects from the \textidentifier{Storage} for later use in
the methods.
In addition to this non-default constructor, the
\vtkmcontinternal{ArrayTransfer} specialization must define the following
items.
\begin{description}
\item[\textcode{ValueType}] A \textcode{typedef} of the type for each item
in the array. This is the same type as the first template argument.
\item[\textcode{PortalControl}] The type of an array portal that is used to
access the underlying data in the control environment.
\item[\textcode{PortalConstControl}] A read-only (const) version of
\textcode{PortalControl}.
\item[\textcode{PortalExecution}] The type of an array portal that is used
to access the underlying data in the execution environment.
\item[\textcode{PortalConstExecution}] A read-only (const) version of
\textcode{PortalExecution}.
\item[\textcode{GetNumberOfValues}] A method that returns the number of
values currently allocated in the execution environment. The results may
be undefined if none of the load or allocate methods have yet been
called.
\item[\textcode{PrepareForInput}] A method responsible for transferring
data from the control to the execution for input.
\textcode{PrepareForInput} has one Boolean argument that controls whether
this transfer should actually take place. When true, data from the
\textidentifier{Storage} object given in the constructor should be
transferred to the execution environment; otherwise the data should not
be copied. An \textidentifier{ArrayTransfer} for a derived array
typically ignores this parameter since the arrays being derived manages
this transfer already. Regardless of the Boolean flag, a
\textcode{PortalConstExecution} is returned.
\item[\textcode{PrepareForInPlace}] A method that behaves just like
\textcode{PrepareForInput} except that the data in the execution
environment is used for both reading and writing so the method returns a
\textidentifier{PortalExecution}. If the array is considered read-only,
which is common for derived arrays, then this method should throw a
\vtkmcont{ErrorControlBadValue}.
\item[\textcode{PrepareForOutput}] A method that takes a size (in a
\vtkm{Id}) and allocates an array in the execution environment of the
specified size. The initial memory can be uninitialized. The method
returns a \textcode{PortalExecution} for the allocated data. If the array
is considered read-only, which is common for derived arrays, then this
method should throw a \vtkmcont{ErrorControlBadValue}.
\item[\textcode{RetrieveOutputData}] This method takes an array storage
pointer (which is the same as that passed to the constructor, but
provided for convenience), allocates memory in the control environment,
and copies data from the execution environment into it. If the derived
array is considered read-only and both \textcode{PrepareForInPlace} and
\textcode{PrepareForOutput} throw exceptions, then this method should
never be called. If it is, then that is probably a bug in
\textidentifier{ArrayHandle}, and it is OK to throw
\vtkmcont{ErrorControlInternal}.
\item[\textcode{Shrink}] A method that adjusts the size of the array in the
execution environment to something that is a smaller size. All the data
up to the new length must remain valid. Typically, no memory is actually
reallocated. Instead, a different end is marked. If the derived array is
considered read-only, then this method should throw a
\vtkmcont{ErrorControlBadValue}.
\item[\textcode{ReleaseResources}] A method that frees any resources
(typically memory) in the execution environment.
\end{description}
Continuing our example derived storage that concatenates two arrays
started in Examples \ref{ex:DerivedArrayPortal} and
\ref{ex:DerivedArrayStorage}, the following provides an
\textidentifier{ArrayTransfer} appropriate for the derived storage.
\vtkmlisting[ex:DerivedArrayTransfer]{\textidentifier{ArrayTransfer} for derived storage of concatenated arrays.}{DerivedArrayTransfer.cxx}
\index{array transfer|)}
\index{array handle!subclassing}
The final step to make a derived storage is to create a mechanism to
construct an \textidentifier{ArrayHandle} with a storage derived from the
desired arrays. This can be done by creating a trivial subclass of
\vtkmcont{ArrayHandle} that simply constructs the array handle to the state
of an existing storage. It uses a protected constructor of
\vtkmcont{ArrayHandle} that accepts a constructed storage.
\vtkmlisting[ex:DerivedArrayHandle]{\textidentifier{ArrayHandle} for derived storage of concatenated arrays.}{DerivedArrayHandle.cxx}
Subclasses of \textidentifier{ArrayHandle} provide constructors that
establish the state of the array handle. All array handle subclasses must
also use either the \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS} macro or the
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} macro. Both of these macros
define the typedefs \textcode{Superclass}, \textcode{ValueType}, and
\textcode{StorageTag} as well as a set of constructors and operators
expected of all \textidentifier{ArrayHandle} classes. The difference
between these two macros is that \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS}
is used in templated classes whereas
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} is used in non-templated
classes.
The \textidentifier{ArrayHandle} subclass in
Example~\ref{ex:DerivedArrayHandle} is templated, so it uses the
\vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS} macro. (The other macro is
described in Section~\ref{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT} on
page~\pageref{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT}). This macro takes three
parameters. The first parameter is the name of the subclass where the macro
is defined, the second parameter is the type of the subclass including the
full template specification, and the third parameter is the immediate
superclass including the full template specification. The second and third
parameters of the macro must be enclosed in parentheses so that the C
pre-processor correctly handles commas in the template specification.
\vtkmcont{ArrayHandleCompositeVector} is an example of a derived array
handle provided by VTK-m. It references some fixed number of other arrays,
pulls a specified component out of each, and produces a new component that
is a tuple of these retrieved components.
\index{derived storage|)}
\index{storage!derived|)}
\index{array handle!derived|)}
\index{array handle!fancy|)}
\index{fancy array handle|)}
\section{Adapting Data Structures}
\label{sec:ArrayHandle:Adapting}
\index{array handle!adapting|(}
\index{storage!adapting|(}
The intention of the storage parameter for \vtkmcont{ArrayHandle} is to
implement the strategy design pattern to enable VTK-m to interface directly
with the data of any third party code source. VTK-m is designed to work
with data originating in other libraries or applications. By creating a new
type of storage, VTK-m can be entirely adapted to new kinds of data
structures.
\begin{commonerrors}
Keep in mind that memory layout used can have an effect on the running
time of algorithms in VTK-m. Different data layouts and memory access can
change cache performance and introduce memory affinity problems. The
example code given in this section will likely have poorer cache
performance than the basic storage provided by VTK-m. However, that might
be an acceptable penalty to avoid data copies.
\end{commonerrors}
In this section we demonstrate the steps required to adapt the array handle
to a data structure provided by a third party. For the purposes of the
example, let us say that some fictitious library named ``foo'' has a simple
structure named \textcode{FooFields} that holds the field values for a
particular part of a mesh, and then maintain the field values for all
locations in a mesh in a \textcode{std::deque} object.
\vtkmlisting{Fictitious field storage used in custom array storage examples.}{FictitiousFieldStorage.cxx}
VTK-m expects separate arrays for each of the fields rather than a single
array containing a structure holding all of the fields. However, rather
than copy each field to its own array, we can create a storage for each
field that points directly to the data in a \textcode{FooFieldsDeque}
object.
The first step in creating an adapter storage is to create a control
environment array portal to the data. This is described in more detail in
Section~\ref{sec:ArrayPortals} and is generally straightforward for simple
containers like this. Here is an example implementation for our
\textcode{FooFieldsDeque} container.
\vtkmlisting[ex:ArrayPortalAdapter]{Array portal to adapt a third-party container to VTK-m.}{ArrayPortalAdapter.cxx}
The next step in creating an adapter storage is to define a tag for the
adapter. We shall call ours
\textcode{StorageTagFooPressure}. Then, we need to create a
specialization of the templated \vtkmcontinternal{Storage}
class. The \textidentifier{ArrayHandle} will instantiate an object using
the array container tag we give it, and we define our own specialization so
that it runs our interface into the code.
\vtkmcontinternal{Storage} has two template arguments: the
base type of the array and the storage tag.
\vtkmlisting{Prototype for \protect\vtkmcontinternal{Storage}.}{StoragePrototype.cxx}
The \vtkmcontinternal{Storage} must define the following items.
\begin{description}
\item[\textcode{ValueType}] A \textcode{typedef} of the type for each item
in the array. This is the same type as the first template argument.