Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hankchilds/vtk-m-user-guide
  • chris.turney/vtk-m-user-guide
  • oruebel/vtk-m-user-guide
  • m-kim/vtk-m-user-guide
  • tberger/vtk-m-user-guide
  • ollielo/vtk-m-user-guide
  • dpugmire/vtk-m-user-guide
  • sujin.philip/vtk-m-user-guide
  • nadavi/vtk-m-user-guide
  • mletter1/vtk-m-user-guide
  • kmorel/vtk-m-user-guide
  • jameskress/vtk-m-user-guide
  • vbolea/vtk-m-user-guide
  • NAThompson/vtk-m-user-guide
  • vtk/vtk-m-user-guide
15 results
Show changes
Commits on Source (14)
Showing
with 518 additions and 574 deletions
......@@ -19,6 +19,7 @@ set(images
images/CameraMovement.pdf
images/CellConnectionsHexahedron.pdf
images/CellConnectionsLine.pdf
images/CellConnectionsPolyLine.pdf
images/CellConnectionsPolygon.pdf
images/CellConnectionsPyramid.pdf
images/CellConnectionsQuadrilateral.pdf
......@@ -116,6 +117,7 @@ set(input_docs
FunctionInterface.tex
WorkletArguments.tex
NewWorkletTypes.tex
CellShapeFigure.tex
# Special inputs that go into verbatim-like arguments
examples/VTKmQuickStart.cmake
)
......
% -*- latex -*-
{
\small
\begin{tabular}{@{}c@{ }c@{ }c@{}}
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsVertex}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsLine}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsPolyLine}} \\
\vtkm{CELL\_SHAPE\_VERTEX} &
\vtkm{CELL\_SHAPE\_LINE} &
\vtkm{CELL\_SHAPE\_POLY\_LINE} \\
\vtkm{CellShapeTagVertex} \index{vertex} &
\vtkm{CellShapeTagLine} \index{line} &
\vtkm{CellShapeTagPolyLine} \index{poly line} \\[2ex]
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsTriangle}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsPolygon}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsQuadrilateral}} \\
\vtkm{CELL\_SHAPE\_TRIANGLE} &
\vtkm{CELL\_SHAPE\_POLYGON} &
\vtkm{CELL\_SHAPE\_QUAD} \\
\vtkm{CellShapeTagTriangle} \index{triangle} &
\vtkm{CellShapeTagPolygon} \index{polygon} &
\vtkm{CellShapeTagQuad} \index{quadrilateral} \\[2ex]
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsTetrahedron}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsHexahedron}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsWedge}} \\
\vtkm{CELL\_SHAPE\_TETRA} &
\vtkm{CELL\_SHAPE\_HEXAHEDRON} &
\vtkm{CELL\_SHAPE\_WEDGE} \\
\vtkm{CellShapeTagTetra} \index{tetrahedron} &
\vtkm{CellShapeTagHexahedron} \index{hexahedron} &
\vtkm{CellShapeTagWedge} \index{wedge} \\[2ex]
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsPyramid}} \\
\vtkm{CELL\_SHAPE\_PYRAMID} \\
\vtkm{CellShapeTagPyramid} \index{pyramid}
\end{tabular}
}
......@@ -59,8 +59,8 @@ If the storage parameter is not explicitly defined, it is set to \vtkmmacro{VTKM
%% 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
%% be set before including any \VTKm header files. Currently the only
%% practical storage provided by \VTKm 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
......@@ -79,7 +79,7 @@ The remainder of this chapter uses the storage mechanism to customize the repres
\label{sec:ImplementingFancyArrays}
Although the behavior of fancy arrays might seem complicated, they are
actually straightforward to implement. VTK-m provides several mechanisms to
actually straightforward to implement. \VTKm provides several mechanisms to
implement fancy arrays.
\subsection{Implicit Array Handles}
......@@ -90,7 +90,7 @@ implement fancy arrays.
\index{implicit array handle|(}
\index{functional array|(}
The generic array handle and storage templating in VTK-m allows for
The generic array handle and storage templating in \VTKm 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
......@@ -101,8 +101,8 @@ requires no storage in memory at all. Such a functional array is called an
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
Specifying a functional or implicit array in \VTKm is straightforward.
\VTKm 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
......@@ -115,11 +115,9 @@ 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.
\VTKm already comes with an implicit array handle named \vtkmcont{ArrayHandleCounting} that can make implicit even numbers as well as other more general counts.
(See Section \ref{sec:CountingArrays} for details.)
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
......@@ -161,16 +159,13 @@ 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.
\label{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT}
The \textidentifier{ArrayHandle} subclass in Example~\ref{ex:ImplicitArrayHandleSubclass} is not templated, so it uses the \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS\_NT} macro.
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.
(The other macro is described in Section~\ref{sec:VTKM_ARRAY_HANDLE_SUBCLASS} on page~\pageref{sec:VTKM_ARRAY_HANDLE_SUBCLASS}).
\index{functional array|)}
\index{implicit array handle|)}
......@@ -191,7 +186,7 @@ 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
Specifying a transformed array in \VTKm is straightforward. \VTKm 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.
......@@ -234,8 +229,6 @@ convenience subclass of \vtkmcont{ArrayHandleTransform} or convenience
\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
......@@ -248,17 +241,13 @@ 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.
\label{sec:VTKM_ARRAY_HANDLE_SUBCLASS}
The \textidentifier{ArrayHandle} subclass in Example~\ref{ex:TransformArrayHandleSubclass} is templated, so it uses the \vtkmmacro{VTKM\_ARRAY\_HANDLE\_SUBCLASS} macro.
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.
(The other macro is described in Section~\ref{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT} on page~\pageref{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT}).
\index{transformed array|)}
\index{array handle!transform|)}
......@@ -267,6 +256,8 @@ pre-processor correctly handles commas in the template specification.
\subsection{Derived Storage}
\label{sec:DerivedStorage}
\fix{Change this to use ArrayHandleDecorator.}
\index{array handle!derived|(}
\index{storage!derived|(}
\index{derived storage|(}
......@@ -448,7 +439,7 @@ 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,
handle provided by \VTKm. 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.
......@@ -468,18 +459,18 @@ is a tuple of these retrieved components.
\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
implement the strategy design pattern to enable \VTKm to interface directly
with the data of any third party code source. \VTKm 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
type of storage, \VTKm 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
time of algorithms in \VTKm. 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
performance than the basic storage provided by \VTKm. However, that might
be an acceptable penalty to avoid data copies.
\end{commonerrors}
......@@ -492,7 +483,7 @@ 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
\VTKm 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}
......@@ -504,7 +495,7 @@ 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}
\vtkmlisting[ex:ArrayPortalAdapter]{Array portal to adapt a third-party container to \VTKm.}{ArrayPortalAdapter.cxx}
The next step in creating an adapter storage is to define a tag for the
adapter. We shall call ours
......@@ -552,7 +543,7 @@ The following provides an example implementation of our adapter to a
\textcode{ArrayPortalFooPressure} provided in
Example~\ref{ex:ArrayPortalAdapter}.
\vtkmlisting{Storage to adapt a third-party container to VTK-m.}{StorageAdapter.cxx}
\vtkmlisting{Storage to adapt a third-party container to \VTKm.}{StorageAdapter.cxx}
\index{array handle!subclassing}
......@@ -562,9 +553,7 @@ storage. This can be done by creating a trivial subclass of
\vtkmcont{ArrayHandle} that simply constructs the array handle to the state
of an existing container.
\vtkmlisting[ex:ArrayHandleAdapter]{Array handle to adapt a third-party container to VTK-m.}{ArrayHandleAdapter.cxx}
\label{sec:VTKM_ARRAY_HANDLE_SUBCLASS_NT}
\vtkmlisting[ex:ArrayHandleAdapter]{Array handle to adapt a third-party container to \VTKm.}{ArrayHandleAdapter.cxx}
Subclasses of \textidentifier{ArrayHandle} provide constructors that
establish the state of the array handle. All array handle subclasses must
......@@ -589,7 +578,7 @@ 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.
With this new version of \textidentifier{ArrayHandle}, VTK-m can now read
With this new version of \textidentifier{ArrayHandle}, \VTKm can now read
to and write from the \textcode{FooFieldsDeque} structure directly.
\vtkmlisting[ex:UsingArrayHandleAdapter]{Using an \textidentifier{ArrayHandle} with custom container.}{UsingArrayHandleAdapter.cxx}
......@@ -604,14 +593,14 @@ to and write from the \textcode{FooFieldsDeque} structure directly.
\index{array handle!storage!default}
\index{storage!default}
Most of the code in VTK-m will create \textidentifier{ArrayHandle}s using
Most of the code in \VTKm will create \textidentifier{ArrayHandle}s using
the default storage, which is set to the basic storage if not otherwise
specified. If you wish to replace the default storage used, then set the
\vtkmmacro{VTKM\_STORAGE} macro to \vtkmmacro{VTKM\_STORAGE\_UNDEFINED} and
set the \vtkmmacro{VTKM\_DEFAULT\_STORAGE\_TAG} to your tag class. These
definitions have to happen \emph{before} including any VTK-m header files.
definitions have to happen \emph{before} including any \VTKm header files.
You will also have to declare the tag class (or at least a prototype of it)
before including VTK-m header files.
before including \VTKm header files.
\begin{vtkmexample}{Redefining the default array handle storage.}
#define VTKM_STORAGE VTKM_STORAGE_UNDEFINED
......@@ -624,7 +613,7 @@ struct StorageTagFooPressure;
\textidentifier{ArrayHandle}s are often stored in dynamic objects like
variant arrays (Chapter~\ref{chap:VariantArrayHandle}) or data sets
(Chapter~\ref{chap:DataSets}). When this happens, the array's type
information, including the storage used, is lost. VTK-m will execute algorithms
information, including the storage used, is lost. \VTKm will execute algorithms
using the \textidentifier{ArrayHandleVirtual} interface. For hot path types
and storages for filters it is good to specify custom sets in the policy when
executing filters.
......
......@@ -122,14 +122,18 @@ spacing along each axis.
\index{explicit mesh}
\index{unstructured grid}
An explicit mesh is an arbitrary collection of cells with arbitrary
connections. It can have multiple different types of cells. Explicit meshes
are also known as unstructured grids.
An explicit mesh is an arbitrary collection of cells with arbitrary connections.
It can have multiple different types of cells.
Explicit meshes are also known as unstructured grids.
Explicit meshes can contain cells of different shapes.
The shapes that \VTKm currently supports are listed in Figure \ref{fig:CreateExplicitMeshesCellShapes}.
The cells of an explicit mesh are defined by providing the shape, number of
indices, and the points that comprise it for each cell. These three things
are stored in separate arrays. Figure~\ref{fig:ExplicitMesh} shows an
example of an explicit mesh and the arrays that can be used to define it.
\begin{figure}[htb]
\centering
\input{CellShapeFigure}
\caption{Basic Cell Shapes}
\label{fig:CreateExplicitMeshesCellShapes}
\end{figure}
\begin{figure}[htb]
\centering
......@@ -138,12 +142,34 @@ example of an explicit mesh and the arrays that can be used to define it.
\label{fig:ExplicitMesh}
\end{figure}
The \vtkmcont{DataSetBuilderExplicit} class can be used to create data sets
with explicit meshes. \textidentifier{DataSetBuilderExplicit} has several
versions of a method named \classmember*{DataSetBuilderExplicit}{Create}. Generally, these methods take
the shapes, number of indices, and connectivity arrays as well as an array
of point coordinates. These arrays can be given in \textcode{std::vector}
objects, and the data are copied into the \textidentifier{DataSet} created.
The cells of an explicit mesh are defined with the following 3 arrays, which are depicted graphically in Figure \ref{fig:ExplicitMesh}.
\begin{description}
\item[Shapes] \index{explicit mesh!shapes}
An array of ids identifying the shape of the cell.
Each value is a \vtkm{UInt8} and should be set to one of the \vtkm{}\textcode{::}\textidentifier{CELL\_SHAPE\_*} constants.
The shapes and their identifiers are shown in Figure \ref{fig:CreateExplicitMeshesCellShapes}.
The size of this array is equal to the number of cells in the set.
\item[Connectivity] \index{explicit mesh!connectivity}
An array that lists all the points that comprise each cell.
Each entry in the array is a \vtkm{Id} giving the point id associated with a vertex of a cell.
The points for each cell are given in a prescribed order for each shape, which is also shown in Figure \ref{fig:CreateExplicitMeshesCellShapes}.
The point indices are stored consecutively from the first cell to the last.
\item[Offsets] \index{explicit mesh!offsets}
An array of \vtkm{Id}s pointing to the index in the connectivity array where the points for a particular cell starts.
The size of this array is equal to the number of cells in the set plus 1.
The first entry is expected to be 0 (since the connectivity of the first cell is at the start of the connectivity array).
The last entry, which does not correspond to any cell, should be the size of the connectivity array.
\end{description}
One important item that is missing from this list of arrays is a count of the number of indices associated with each cell.
This is not explicitly represented in \VTKm's mesh structure because it can be implicitly derived from the offsets array by subtracting consecutive entries.
However, it is usually the case when building an explicit mesh that you will have an array of these counts rather than the offsets.
It is for this reason that \VTKm contains mechanisms to build an explicit data set with a ``num indices'' arrays rather than an offsets array.
The \vtkmcont{DataSetBuilderExplicit} class can be used to create data sets with explicit meshes.
\textidentifier{DataSetBuilderExplicit} has several versions of a method named \classmember*{DataSetBuilderExplicit}{Create}.
Generally, these methods take the shapes, number of indices, and connectivity arrays as well as an array of point coordinates.
These arrays can be given in \textcode{std::vector} objects, and the data are copied into the \textidentifier{DataSet} created.
The following example creates a mesh like the one shown in
Figure~\ref{fig:ExplicitMesh}.
......@@ -276,32 +302,36 @@ on page~\pageref{sec:DataSets:CoordinateSystems}.
\index{explicit cell set|(}
\index{cell set!explicit|(}
A \vtkmcont{CellSetExplicit} defines an irregular collection of cells. The
cells can be of different types and connected in arbitrary ways. This is
done by explicitly providing for each cell a sequence of points that
defines the cell.
A \vtkmcont{CellSetExplicit} defines an irregular collection of cells.
The cells can be of different types and connected in arbitrary ways.
The types of cell sets are listed in Figure \ref{fig:ExplicitCellSetShapes}.
This is done by explicitly providing for each cell a sequence of points that defines the cell.
\begin{figure}[htb]
\centering
\input{CellShapeFigure}
\caption{Basic Cell Shapes in a \textidentifier{CellSetExplicit}.}
\label{fig:ExplicitCellSetShapes}
\end{figure}
An explicit cell set is defined with a minimum of three arrays. The first
array identifies the shape of each cell. (Cell shapes are discussed in
detail in Section~\ref{sec:CellShapeTagsIds} starting on
page~\pageref{sec:CellShapeTagsIds}.) The second array identifies how many
points are in each cell. The third array has a sequence of point indices
that make up each cell. Figure~\ref{fig:CellSetExplicit} shows a simple
example of an explicit cell set.
An explicit cell set is defined with a minimum of three arrays.
The first array identifies the shape of each cell.
(Identifiers for cell shapes are shown in Figure \ref{fig:ExplicitCellSetShapes}.)
The second array has a sequence of point indices that make up each cell.
The third array identifies an offset into the second array where the point indices for each cell is found plus an extra entry at the end set to the size of the second array.
Figure~\ref{fig:CellSetExplicit} shows a simple example of an explicit cell set.
\begin{figure}[htb]
\centering
\includegraphics{images/ExplicitCellConnections}
\caption{Example of cells in a \textidentifier{CellSetExplict} and the
\caption{Example of cells in a \textidentifier{CellSetExplicit} and the
arrays that define them.}
\label{fig:CellSetExplicit}
\end{figure}
An explicit cell set may also have other topological arrays such as an
array of offsets of each cell into the connectivity array or an array of
cells incident on each point. Although these arrays can be provided, they
are optional and can be internally derived from the shape, num indices, and
connectivity arrays.
An explicit cell set can also identify the number of indices defined for each cell by subtracting consecutive entries in the offsets array.
It is often the case when creating a \textidentifier{CellSetExplicit} that you have an array containing the number of indices rather than the offsets.
Such an array can be converted to an offsets array that can be used with \textidentifier{CellSetExplicit} by using the \vtkmcont{ConvertNumIndicesToOffsets} convenience function.
\vtkmcont{ExplicitCellSet} is a powerful representation for a cell set
because it can represent an arbitrary collection of cells. However, because
......
......@@ -5,19 +5,15 @@
\index{function interface|(}
For flexibility's sake a worklet is free to declare a \controlsignature
with whatever number of arguments are sensible for its operation. The
\classmember*{DispatcherBase}{Invoke} method of the dispatcher is expected to support arguments
that match these arguments, and part of the dispatching operation may
require these arguments to be augmented before the worklet is
scheduled. This leaves dispatchers with the tricky task of managing some
collection of arguments of unknown size and unknown types.
\fix{\textidentifier{FunctionInterface} is in the \vtkminternal{}
interface. I still can't decide if it should be moved to the \vtkm{}
interface.}
To simplify this management, VTK-m has the \vtkminternal{FunctionInterface}
For flexibility's sake a worklet is free to declare a \controlsignature with whatever number of arguments are sensible for its operation.
The \textidentifier{Invoker} is expected to support arguments that match these arguments, and part of the invocation operation may require these arguments to be augmented before the worklet is scheduled.
This leaves the invoker with the tricky task of managing some collection of arguments of unknown size and unknown types.
\fix{\textidentifier{FunctionInterface} is in the \vtkminternal{} interface.
I still can't decide if it should be moved to the \vtkm{} interface.
Update: I'm glad I have not moved \textidentifier{FunctionInterface} ro \vtkm{} because now we are thinking of getting rid of it.}
To simplify this management, \VTKm has the \vtkminternal{FunctionInterface}
class. \textidentifier{FunctionInterface} is a templated class that manages
a generic set of arguments and return value from a function. An instance of
\textidentifier{FunctionInterface} holds an instance of each argument. You
......
......@@ -47,9 +47,9 @@ We will see this momentarily.
\end{didyouknow}
The second and final worklet we need to generate our wireframe cells is one that outputs the indices of an edge.
The worklet parenthesis's operator takes information about the input cell (shape and point indices) and an index of which edge to output.
The worklet parenthesis' operator takes information about the input cell (shape and point indices) and an index of which edge to output.
The aforementioned \textidentifier{ScatterCounting} provides a \sigtag{VisitIndex} that signals which edge to output.
The worklet parenthesis operator returns the two indices for the line in, naturally enough, a \vtkm{Vec}\tparams{\vtkm{Id},2}.
The worklet parenthesis operator returns the two indices for the line in, naturally enough, a \vtkm{Id2}.
\vtkmlisting[ex:GenerateMeshConstantShapeGenIndices]{A worklet to generate indices for line cells.}{GenerateMeshConstantShapeGenIndices.cxx}
......@@ -66,18 +66,18 @@ We could jump through some hoops adjusting the \textidentifier{ScatterCounting}
But that would be overly complicated and inefficient.
A simpler approach is to use the \vtkmcont{ArrayHandleGroupVec} fancy array handle (described in Section~\ref{sec:GroupedVectorArrays}) to make a flat array of indices look like an array of \textidentifier{Vec} objects.
The following example shows a \textcode{Run} method in a worklet helper class.
Note the use of \textidentifier{make\_ArrayHandleGroupVec} when calling the \classmember*{DispatcherMapTopology}{Invoke} method to make this conversion.
The following example shows what the \classmember*{FilterDataSet}{DoExecute} method in the associated filter would look like.
Note the use \textidentifier{make\_ArrayHandleGroupVec} when calling \classmember*{FilterDataSet}{Invoke} on line \ref{ex:GenerateMeshConstantShapeInvoke.cxx:InvokeEdgeIndices} to make this conversion.
\vtkmlisting[ex:GenerateMeshConstantShapeInvoke]{Invoking worklets to extract edges from a cell set.}{GenerateMeshConstantShapeInvoke.cxx}
Another feature to note in Example~\ref{ex:GenerateMeshConstantShapeInvoke} is that the method calls \classmember*{ScatterCounting}{GetOutputToInputMap} on the \textidentifier{Scatter} object it creates and squirrels it away for later use.
The reason for this behavior is to implement mapping fields that are attached to the input cells to the indices of the output.
In practice, this worklet is going to be called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
The method in Example~\ref{ex:GenerateMeshConstantShapeInvoke} creates a new \textidentifier{CellSet}, but we also need a method to transform the \textidentifier{Field}s on the data set.
Another feature to note in Example~\ref{ex:GenerateMeshConstantShapeInvoke} is that the method calls \classmember*{ScatterCounting}{GetOutputToInputMap} on the \textidentifier{Scatter} object it creates and squirrels the map array away for later use.
The reason for this behavior is to implement mapping fields that are attached on the input cells to the indices of the output.
In practice, \classmember*{FilterDataSet}{DoExecute} is called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
The method in Example \ref{ex:GenerateMeshConstantShapeInvoke} creates a new \textidentifier{CellSet}, but we also need a method to transform the \textidentifier{Field}s on the data set.
The saved \textcode{OutputToInputCellMap} array allows us to transform input fields to output fields.
The following example shows another convenience method that takes this saved \textcode{OutputToInputCellMap} array and converts an array from an input cell field to an output cell field array.
The following example shows the implementation of \classmember*{FilterDataSet}{DoMapField} in the associated filter that takes this saved \textcode{OutputToInputCellMap} array and converts an array from an input cell field to an output cell field array.
Note that we can do this by using the \textcode{OutputToInputCellMap} as an index array in a \vtkmcont{ArrayHandlePermutation}.
\vtkmlisting[ex:GenerateMeshConstantShapeMapCellField]{Converting cell fields using a simple permutation.}{GenerateMeshConstantShapeMapCellField.cxx}
......@@ -124,27 +124,27 @@ The reduce by key worklet can then group the edges by the key and allow you to c
Thus, our goal of finding duplicate edges hinges on producing a key where two keys are identical if and only if the edges are the same.
One straightforward key is to use the coordinates in 3D space by, say, computing the midpoint of the edge.
The main problem with using point coordinates approach is that a computer can hold a point coordinate only with floating point numbers of limited precision.
The main problem with using this point coordinates approach is that a computer can hold a point coordinate only with floating point numbers of limited precision.
Computer floating point computations are notorious for providing slightly different answers when the results should be the same.
For example, if an edge as endpoints at $p_1$ and $p_2$ and two different cells compute the midpoint as $(p_1+p_2)/2$ and $(p_2+p_1)/2$, respectively, the answer is likely to be slightly different.
For example, if an edge has endpoints at $p_1$ and $p_2$ and two different cells compute the midpoint as $(p_1+p_2)/2$ and $(p_2+p_1)/2$, respectively, the answer is likely to be slightly different.
When this happens, the keys will not be the same and we will still produce 2 edges in the output.
Fortunately, there is a better choice for keys based on the observation that in the original cell set each edge is specified by endpoints that each have unique indices.
We can combine these 2 point indices to form a ``canonical'' descriptor of an edge (correcting for order).%
\footnote{Using indices to find common mesh elements is described by Miller et al. in ``Finely-Threaded History-Based Topology Computation'' (in \emph{Eurographics Symposium on Parallel Graphics and Visualization}, June 2014).}
\VTKm comes with a helper function, \vtkmexec{CellEdgeCanonicalId}, defined in \vtkmheader{vtkm/exec}{CellEdge.h} to produce these unique edge keys as \vtkm{Id2}s.
\VTKm comes with a helper function, \vtkmexec{CellEdgeCanonicalId}, defined in \vtkmheader{vtkm/exec}{CellEdge.h}, to produce these unique edge keys as \vtkm{Id2}s.
Our second worklet produces these canonical edge identifiers.
\vtkmlisting[ex:GenerateMeshCombineLikeGenIds]{Worklet generating canonical edge identifiers.}{GenerateMeshCombineLikeGenIds.cxx}
Our third and final worklet generates the line cells by outputting the indices of each edge.
As hinted at earlier, this worklet is a reduce by key worklet (inheriting from \vtkmworklet{WorkletReduceByKey}).
The reduce by key dispatcher will collect the unique keys and call the worklet once for each unique edge.
When the worklet is invoked, \VTKm will collect the unique keys and call the worklet once for each unique edge.
Because there is no longer a consistent mapping from the generated lines to the elements of the input cell set, we need pairs of indices identifying the cells/edges from which the edge information comes.
We use these indices along with a connectivity structure produced by a \sigtag{WholeCellSetIn} to find the information about the edge.
As shown later, these indices of cells and edges can be extracted from the \textidentifier{ScatterCounting} used to executed the worklet back in Example~\ref{ex:GenerateMeshCombineLikeGenIds}.
As shown later, these indices of cells and edges can be extracted from the \textidentifier{ScatterCounting} used to execute the worklet back in Example~\ref{ex:GenerateMeshCombineLikeGenIds}.
As we did in Section~\ref{sec:GeneratingCellSets:SingleType}, this worklet writes out the edge information in a \vtkm{Vec}\tparams{\vtkm{Id},2} (which in some following code will be created with an \textidentifier{ArrayHandleGroupVec}).
As we did in Section~\ref{sec:GeneratingCellSets:SingleType}, this worklet writes out the edge information in a \vtkm{Id2} (which in some following code will be created with an \textidentifier{ArrayHandleGroupVec}).
\vtkmlisting[ex:GenerateMeshCombineLikeGenIndices]{A worklet to generate indices for line cells from combined edges.}{GenerateMeshCombineLikeGenIndices.cxx}
......@@ -163,22 +163,22 @@ The cell shape and number of points are predetermined to be line and 2, respecti
The last item, the array of connection indices, is what we are creating with the worklet in Example~\ref{ex:GenerateMeshCombineLikeGenIndices}.
The connectivity array for \textidentifier{CellSetSingleType} is expected to be a flat array of \vtkm{Id} indices, but the worklet needs to provide groups of indices for each cell (in this case as a \textidentifier{Vec} object).
To reconcile what the worklet provides and what the connectivity array must look like, we use the \vtkmcont{ArrayHandleGroupVec} fancy array handle (described in Section~\ref{sec:GroupedVectorArrays}) to make a flat array of indices look like an array of \textidentifier{Vec} objects.
The following example shows a \textcode{Run} method in a worklet helper class.
Note the use of \textidentifier{make\_ArrayHandleGroupVec} when calling the \classmember*{DispatcherMapTopology}{Invoke} method to make this conversion.
The following example shows what the \classmember*{FilterDataSet}{DoExecute} method in the associated filter would look like.
Note the use of \textidentifier{make\_ArrayHandleGroupVec} when calling \classmember*{FilterDataSet}{Invoke} on line \ref{ex:GenerateMeshCombineLikeInvoke.cxx:InvokeEdgeIndices} to make this conversion.
\vtkmlisting[ex:GenerateMeshCombineLikeInvoke]{Invoking worklets to extract unique edges from a cell set.}{GenerateMeshCombineLikeInvoke.cxx}
Another feature to note in Example~\ref{ex:GenerateMeshCombineLikeInvoke} is that the method calls \classmember*{ScatterCounting}{GetOutputToInputMap} on the \textidentifier{Scatter} object it creates and squirrels it away for later use.
It also saves the \vtkmworklet{Keys} object created for later user.
The reason for this behavior is to implement mapping fields that are attached to the input cells to the indices of the output.
In practice, these worklet are going to be called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
The method in Example~\ref{ex:GenerateMeshCombineLikeInvoke} creates a new \textidentifier{CellSet}, but we also need a method to transform the \textidentifier{Field}s on the data set.
It also saves the \vtkmworklet{Keys} object created for later use.
The reason for this behavior is to implement mapping fields that are attached on the input cells to the indices of the output.
In practice, \classmember*{FilterDataSet}{DoExecute} is called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
The method in Example \ref{ex:GenerateMeshCombineLikeInvoke} creates a new \textidentifier{CellSet}, but we also need a method to transform the \textidentifier{Field}s on the data set.
The saved \textcode{OutputToInputCellMap} array and \textidentifier{Keys} object allow us to transform input fields to output fields.
The following example shows another convenience method that takes these saved objects and converts an array from an input cell field to an output cell field array.
The following example shows the implementation of \classmember*{FilterDataSet}{DoMapField} in the associated filter that takes these saved objects and converts an array from an input cell field to an output cell field array.
Because in general there are several cells that contribute to each edge/line in the output, we need a method to combine all these cell values to one.
The most appropriate combination is likely an average of all the values.
Because this is a common operation, \VTKm provides the \vtkmworklet{AverageByKey} to help perform exactly this operation.
Because this is a common operation, \VTKm provides the \vtkmworklet{AverageByKey} class to help perform exactly this operation.
\textidentifier{AverageByKey} provides a \textcode{Run} method that takes a \textidentifier{Keys} object, an array of in values, and a device adapter tag and produces and array of values averaged by key.
\vtkmlisting[ex:GenerateMeshCombineLikeMapCellField]{Converting cell fields that average collected values.}{GenerateMeshCombineLikeMapCellField.cxx}
......@@ -208,8 +208,8 @@ This hash function uses the FNV-1a algorithm that is designed to create hash val
This means that hash values of two different identifiers are unlikely to be the same.
That said, hash collisions can happen and become increasingly likely on larger data sets.
Therefore, if we wish to use hash values, we also have to add conditions that manage when collisions happen.
Resolving hash value collisions adds overhead, but time saved in faster sorting of hash values generally outweighs the overhead added by resolving collisions.%
Therefore, if we wish to use hash values, we also have to add conditions that manage collisions when they happen.
Resolving hash value collisions adds overhead, but the time saved in faster sorting of hash values generally outweighs the overhead added by resolving collisions.%
\footnote{A comparison of the time required for completely unique keys and hash keys with collisions is studied by Lessley, et al. in ``Techniques for Data-Parallel Searching for Duplicate Elements'' (in \emph{IEEE Symposium on Large Data Analysis and Visualization}, October 2017).}
In this section we will improve on the implementation given in Section~\ref{sec:GeneratingCellSets:CombiningLikeElements} by using hash values for keys and resolving for collisions.
......@@ -234,10 +234,10 @@ It identifies which edges are actually the same as which other edges, marks a lo
\vtkmlisting[ex:GenerateMeshHashResolveCollisions]{Worklet to resolve hash collisions occurring on edge identifiers.}{GenerateMeshHashResolveCollisions.cxx}
With all hash collisions correctly identified, we are ready to generate the connectivity array for the line elements.
This worklet uses a reduce by key dispatch like the previous example, but this time we use a \textidentifier{ScatterCounting} to run the worklet multiple times for hash values that contain multiple unique edges.
This worklet uses a reduce by key worklet like the previous example, but this time we use a \textidentifier{ScatterCounting} to run the worklet multiple times for hash values that contain multiple unique edges.
The worklet takes all the information it needs to reference back to the edges in the original mesh including a \sigtag{WholeCellSetIn}, look back indices for the cells and respective edges, and the unique edge group indicators produced by Example~\ref{ex:GenerateMeshHashGenHashes}.
As in the previous sections, this worklet writes out the edge information in a \vtkm{Vec}\tparams{\vtkm{Id},2} (which in some following code will be created with an \textidentifier{ArrayHandleGroupVec}).
As in the previous sections, this worklet writes out the edge information in a \vtkm{Id2} (which in some following code will be created with an \textidentifier{ArrayHandleGroupVec}).
\vtkmlisting[ex:GenerateMeshHashGenIndices]{A worklet to generate indices for line cells from combined edges and potential collisions.}{GenerateMeshHashGenIndices.cxx}
......@@ -247,22 +247,22 @@ The first 3 items are trivial.
The number of points can be taken from the input cell set as they are the same.
The cell shape and number of points are predetermined to be line and 2, respectively.
The last item, the array of connection indices, is what we are creating with the worklet in Example~\ref{ex:GenerateMeshCombineLikeGenIndices}.
The last item, the array of connection indices, is what we are creating with the worklet in Example \ref{ex:GenerateMeshHashGenIndices}.
The connectivity array for \textidentifier{CellSetSingleType} is expected to be a flat array of \vtkm{Id} indices, but the worklet needs to provide groups of indices for each cell (in this case as a \textidentifier{Vec} object).
To reconcile what the worklet provides and what the connectivity array must look like, we use the \vtkmcont{ArrayHandleGroupVec} fancy array handle (described in Section~\ref{sec:GroupedVectorArrays}) to make a flat array of indices look like an array of \textidentifier{Vec} objects.
The following example shows a \textcode{Run} method in a worklet helper class.
Note the use of \textidentifier{make\_ArrayHandleGroupVec} when calling the \classmember*{DispatcherMapTopology}{Invoke} method to make this conversion.
The following example shows what the \classmember*{FilterDataSet}{DoExecute} method in the associated filter would look like.
\vtkmlisting[ex:GenerateMeshHashInvoke]{Invoking worklets to extract unique edges from a cell set using hash values.}{GenerateMeshHashInvoke.cxx}
As noted in Section~\ref{sec:GeneratingCellSets:CombiningLikeElements}, in practice these worklets are going to be called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
As noted in Section~\ref{sec:GeneratingCellSets:CombiningLikeElements}, in practice \classmember*{FilterDataSet}{DoExecute} is called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
Although Example~\ref{ex:GenerateMeshHashInvoke} creates a new \textidentifier{CellSet}, we also need a method to transform the \textidentifier{Field}s on the data set.
To do this, we need to save some information.
This includes the map from the edge of each cell to its origin cell (\textcode{OutputToInputCellMap}), a \textidentifier{Keys} object on the hash values (\textcode{CellToEdgeKeys}), an array of indices resolving collisions (\textcode{LocalEdgeIndices}), and a \textidentifier{ScatterCounting} to repeat edge outputs when unique edges collide on a hash (\textcode{HashCollisionScatter}).
\begin{commonerrors}
Note that \textcode{HashCollisionScatter} is an object of type \vtkmworklet{ScatterCounting}, which does not have a default constructor.
That can be a problem since you do not have the data to construct \textcode{HashCollisionScatter} until \textcode{Run} is called.
That can be a problem since you do not have the data to construct \textcode{HashCollisionScatter} until \classmember*{FilterDataSet}{DoExecute} is called.
To get around this chicken-and-egg issue, it is best to store the \textidentifier{ScatterCounter} as a pointer.
It is best practice to use a smart pointer, like \textcode{std::shared\_ptr} to manage it.
\end{commonerrors}
......@@ -290,7 +290,7 @@ However, it can also be the case where an algorithm must create cells of a diffe
The procedure for generating cells of different shapes is similar to that of creating a single shape.
There is, however, an added step of counting the size (in number of points) of each shape to build the appropriate structure for storing the cell connectivity.
Our motivating example is a set of worklets that extracts all the unique faces in a cell set and stores them in a cell set of polygons.
Our motivating example is a filter that extracts all the unique faces in a cell set and stores them in a cell set of polygons.
This problem is similar to the one addressed in Sections \ref{sec:GeneratingCellSets:SingleType}, \ref{sec:GeneratingCellSets:CombiningLikeElements}, and \ref{sec:GeneratingCellSets:FasterCombiningLikeElements}.
In both cases it is necessary to find all subelements of each cell (in this case the faces instead of the edges).
It is also the case that we expect many faces to be shared among cells in the same way edges are shared among cells.
......@@ -320,7 +320,7 @@ Instead, for this use case we need to use \vtkmcont{ArrayHandleGroupVecVariable}
As described in Section~\ref{sec:GroupedVectorArrays}, \textidentifier{ArrayHandleGroupVecVariable} takes a flat array of values and an index array of offsets that points to the beginning of each group to represent as a \Veclike.
The worklet in Example~\ref{ex:GenerateMeshVariableShapeCountPointsInFace} does not actually give us the array of offsets we need.
Rather, it gives us the count of each group.
We can get the offsets from the counts by using the \vtkmcont{ConvertNumComponentsToOffsets} convenience function.
We can get the offsets from the counts by using the \vtkmcont{ConvertNumIndicesToOffsets} convenience function.
\vtkmlisting[ex:GenerateMeshVariableShapeOffsetsArray]{Converting counts of connectivity groups to offsets for \textidentifier{ArrayHandleGroupVecVariable}.}{GenerateMeshVariableShapeOffsetsArray.cxx}
......@@ -329,11 +329,11 @@ The worklet is very similar to the one for creating edge lines (shown in Example
\vtkmlisting[ex:GenerateMeshVariableShapeGenIndices]{A worklet to generate indices for polygon cells of different sizes from combined edges and potential collisions.}{GenerateMeshVariableShapeGenIndices.cxx}
With these worklets in place, we can use the following helper method to execute the series of worklets to extract unique faces.
With these worklets in place, we can implement a filter \classmember*{FilterDataSet}{DoExecute} as follows.
\vtkmlisting[ex:GenerateMeshVariableShapeInvoke]{Invoking worklets to extract unique faces froma cell set.}{GenerateMeshVariableShapeInvoke.cxx}
\vtkmlisting[ex:GenerateMeshVariableShapeInvoke]{Invoking worklets to extract unique faces from a cell set.}{GenerateMeshVariableShapeInvoke.cxx}
As noted previously, in practice these worklets are going to be called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
As noted previously, in practice \classmember*{FilterDataSet}{DoExecute} is called on \textidentifier{DataSet} objects to create new \textidentifier{DataSet} objects.
The process for doing so is no different from our previous algorithm as described at the end of Section~\ref{sec:GeneratingCellSets:FasterCombiningLikeElements} (Examples \ref{ex:GenerateMeshHashAverageField} and \ref{ex:GenerateMeshHashMapCellField}).
\index{CellSetExplicit!generate|)}
......
......@@ -6,20 +6,20 @@
\index{device adapter|(}
\index{device adapter!implementing|(}
VTK-m comes with several implementations of device adapters so that it may be ported to a variety of platforms.
\VTKm comes with several implementations of device adapters so that it may be ported to a variety of platforms.
It is also possible to provide new device adapters to support yet more devices, compilers, and libraries.
A new device adapter provides a tag, a class to manage arrays in the execution environment, a class to establish virtual objects in the execution environment, a collection of algorithms that run in the execution environment, and (optionally) a timer.
Most device adapters are associated with some type of device or library, and all source code related directly to that device is placed in a subdirectory of \textfilename{vtkm/cont}.
For example, files associated with CUDA are in \textfilename{vtkm/cont/cuda}, files associated with the Intel Threading Building Blocks (TBB) are located in \textfilename{vtkm/cont/tbb}, and files associated with OpenMP are in \textfilename{vtkm/cont/openmp}.
The documentation here assumes that you are adding a device adapter to the VTK-m source code and following these file conventions.
The documentation here assumes that you are adding a device adapter to the \VTKm source code and following these file conventions.
For the purposes of discussion in this section, we will give a simple
example of implementing a device adapter using the \textcode{std::thread}
class provided by C++11. We will call our device \textcode{Cxx11Thread} and
place it in the directory \textfilename{vtkm/cont/cxx11}.
By convention the implementation of device adapters within VTK-m are divided into 6 header files with the names \textfilename{DeviceAdapterTag\textasteriskcentered.h}, \textfilename{DeviceAdapterRuntimeDetector\textasteriskcentered.h}, \textfilename{ArrayManagerExecution\textasteriskcentered.h}, \textfilename{VirtualObjectTransfer\textasteriskcentered.h}, \textfilename{AtomicInterfaceExecution\textasteriskcentered.h} and \textfilename{DeviceAdapterAlgorithm\textasteriskcentered.h}, which are hidden in internal directories.
By convention the implementation of device adapters within \VTKm are divided into 6 header files with the names \textfilename{DeviceAdapterTag\textasteriskcentered.h}, \textfilename{DeviceAdapterRuntimeDetector\textasteriskcentered.h}, \textfilename{ArrayManagerExecution\textasteriskcentered.h}, \textfilename{VirtualObjectTransfer\textasteriskcentered.h}, \textfilename{AtomicInterfaceExecution\textasteriskcentered.h} and \textfilename{DeviceAdapterAlgorithm\textasteriskcentered.h}, which are hidden in internal directories.
The \textfilename{DeviceAdapter\textasteriskcentered.h} that most code includes is a trivial header that simply includes these other 6 files.
For our example \textcode{std::thread} device, we will create the base header at \textfilename{vtkm/cont/cxx11/DeviceAdapterCxx11Thread.h}.
The contents are the following (with minutia like include guards removed).
......@@ -33,7 +33,7 @@ The contents are the following (with minutia like include guards removed).
#include <vtkm/cont/cxx11/internal/DeviceAdapterAlgorithmCxx11Thread.h>
\end{vtkmexample}
The reason VTK-m breaks up the code for its device adapters this way is
The reason \VTKm breaks up the code for its device adapters this way is
that there is an interdependence between the implementation of each device
adapter and the mechanism to pick a default device adapter. Breaking up the
device adapter code in this way maintains an acyclic dependence among
......@@ -53,11 +53,11 @@ prefix of \textfilename{DeviceAdapterTag}.
The device adapter tag should be created with the macro
\vtkmmacro{VTKM\_VALID\_DEVICE\_ADAPTER}. This adapter takes an abbreviated
name that it will append to \textcode{DeviceAdapterTag} to make the tag
structure. It will also create some support classes that allow VTK-m to
structure. It will also create some support classes that allow \VTKm to
introspect the device adapter. The macro also expects a unique integer
identifier that is usually stored in a macro prefixed with
\textcode{VTKM\_DEVICE\_ADAPTER\_}. These identifiers for the device
adapters provided by the core VTK-m are declared in
adapters provided by the core \VTKm are declared in
\vtkmheader{vtkm/cont/internal}{DeviceAdapterTag.h}.
The following example gives the implementation of our custom device
......@@ -161,7 +161,7 @@ data from control array portals.
However, if the control and execution environments share the same memory
space, the execution array manager can, and should, delegate all of its
operations to the \textidentifier{Storage} it is constructed with. VTK-m
operations to the \textidentifier{Storage} it is constructed with. \VTKm
comes with a class called
\vtkmcontinternal{ArrayManagerExecutionShareWithControl} that provides the
implementation for an execution array manager that shares a memory space
......@@ -281,7 +281,7 @@ Continuing our example of a device adapter based on C++11's \textcode{std::threa
\index{virtual object transfer|(}
\index{transfer virtual object|(}
VTK-m defines a template named \vtkmcontinternal{VirtualObjectTransfer} that is responsible for instantiating virtual objects in the execution environment.
\VTKm defines a template named \vtkmcontinternal{VirtualObjectTransfer} that is responsible for instantiating virtual objects in the execution environment.
\fix{Re-add the following after it is implemented.}
%% Chapter~\ref{chap:VirtualObjects} discusses how to design and implement virtual objects.
The \textidentifier{VirtualObjectTransfer} class is the internal mechanism that allocates space for the object and sets up the virtual method tables for them.
......@@ -324,7 +324,7 @@ It can be assumed that the object can be trivially copied (with the exception of
\end{didyouknow}
However, if the control and execution environments share the same memory space, the virtual object transfer can, and should, just bind directly with the target concrete object.
VTK-m comes with a class called \vtkmcontinternal{VirtualObjectTransferShareWithControl} that provides the implementation for a virtual object transfer that shares a memory space with the control environment.
\VTKm comes with a class called \vtkmcontinternal{VirtualObjectTransferShareWithControl} that provides the implementation for a virtual object transfer that shares a memory space with the control environment.
In this case, making the \textidentifier{VirtualObjectTransfer} specialization be a trivial subclass is sufficient.
Continuing our example of a device adapter based on C++11's \textcode{std::thread} class, here is the implementation of \textidentifier{VirtualObjectTransfer}, which by convention would be placed in the \textfilename{vtkm/cont/cxx11/internal/VirtualObjectTransferCxx11Thread.h} header file.
......@@ -339,7 +339,7 @@ Continuing our example of a device adapter based on C++11's \textcode{std::threa
\index{device adapter!atomic interface execution|(}
\index{atomic interface execution|(}
VTK-m defines a template named \vtkmcontinternal{AtomicInterfaceExecution} that is responsible for performing atomic operations on raw addresses.
\VTKm defines a template named \vtkmcontinternal{AtomicInterfaceExecution} that is responsible for performing atomic operations on raw addresses.
\vtkmcontinternal{AtomicInterfaceExecution} defines a \textidentifier{WordTypePreferred} member that is the fastest available for bitwise operations of the given device.
At minimum, the interface must support operations on \textidentifier{WordTypePreferred} and \vtkm{WordTypeDefault}, which may be the same.
A full list of the supported word types is advertised in the type list stored in \textidentifier{WordTypes}.
......@@ -379,7 +379,7 @@ A device adapter implementation must also provide a specialization of \vtkmcont{
The implementation for the device adapter algorithms is typically placed in a header file with a prefix of \textfilename{DeviceAdapterAlgorithm}.
Although there are many methods in \textidentifier{DeviceAdapterAlgorithm},
it is seldom necessary to implement them all. Instead, VTK-m comes with
it is seldom necessary to implement them all. Instead, \VTKm comes with
\vtkmcontinternal{DeviceAdapterAlgorithmGeneral} that provides generic
implementation for most of the required algorithms. By deriving the
specialization of \textidentifier{DeviceAdapterAlgorithm} from
......@@ -447,7 +447,7 @@ header file.
\index{timer|(}
\index{device adapter!timer|(}
The VTK-m timer, described in Chapter~\ref{chap:Timers}, delegates to an
The \VTKm timer, described in Chapter~\ref{chap:Timers}, delegates to an
internal class named \vtkmcont{DeviceAdapterTimerImplementation}. The
interface for this class is the same as that for \vtkmcont{Timer}. A default
implementation of this templated class uses the system timer and the
......
......@@ -164,10 +164,16 @@ An example \vtkm{Frustum} is shown in Figure~\ref{fig:ImplicitFrustum}.
\index{implicit functions!handle|(}
To support \VTKm's device agnostic execution environment, \VTKm also provides a \vtkmcont{ImplicitFunctionHandle}.
\vtkmcont{ImplicitFunctionHandle} should be used when executing on a device.
See Chapter \ref{chap:GeneralApproach} for more information on \VTKm's execution environment.
\VTKm also provides some convenience functions for easily creating \vtkmcont{ImplicitFunctionHandle}.
The following example demostrantes how to create a \vtkmcont{ImplicitFunctionHandle} and apply it over a series of points.
The \textidentifier{ImplicitFunction} that your code creates on the host processor of your computer might not work as is on a device that a filter executes on.
Thus the \textidentifier{ImplicitFunction} object needs to be transformed before it is ready for running in a parallel algorithm.
This transformation is managed by the \vtkmcont{ImplicitFunctionHandle} class.
\textidentifier{ImplicitFunctionHandle} holds a reference to an \textidentifier{ImplicitFunction}.
The reference of \textidentifier{ImplicitFunctionHandle} is not dependent on the type of \textidentifier{ImplicitFunction} being held, so it is a convenient way to accept a user-defined function.
\textidentifier{ImplicitFunctionHandle} also manages the transfer of the \textidentifier{ImplicitFunction} to the device.
An \textidentifier{ImplicitFunctionHandle} is easily created with the \vtkmcont{make\_ImplicitFunctionHandle} function.
The following example demonstrates creating an \textidentifier{ImplicitFunction} and then putting it into an \textidentifier{ImplicitFunctionHandle} for use with the \vtkmfilter{ClipWithImplicitFunction} filter.
\vtkmlisting{Using \textidentifier{ImplicitFunctionHandle}.}{ImplicitFunctionHandle.cxx}
......
......@@ -197,6 +197,7 @@
DataSetBuilderRectilinear,
DataSetBuilderUniform,
DataSetFieldAdd,
ConvertNumIndicesToOffsets,
CellLocator,CellLocatorGeneral,
CellLocatorUniformGrid,CellLocatorRectilinearGrid,
CellLocatorUniformBins,CellLocatorBoundingIntervalHierarchy,
......
......@@ -6,27 +6,25 @@
\index{try execute|(}
\index{device adapter!try execute|(}
Throughout this chapter and elsewhere in this book we have seen examples that require specifying the device on which to run using a device adapter tag.
This is an important aspect when writing portable parallel algorithms.
However, it is often the case that users of these algorithms are agnostic about what device \VTKm algorithms run so long as they complete correctly and as fast as possible.
Thus, rather than directly specify a device adapter, you would like \VTKm to try using the best available device, and if that does not work try a different device.
Because of this, there are many features in \VTKm that behave this way.
For example, you may have noticed that running filters, as in the examples of Chapter~\ref{chap:RunningFilters}, you do not need to specify a device; they choose a device for you.
Internally, the filter superclasses have a mechanism to automatically select a device, try it, and fall back to other devices if the first one fails.
Most operations in \VTKm do not require specifying on which device to run.
For example, you may have noticed that when using \vtkmcont{Invoker} to execute a worklet, you do not need to specify a device; it chooses a device for you.
Internally, the \textidentifier{Invoker} has a mechanism to automatically select a device, try it, and fall back to other devices if the first one fails.
We saw this at work in the implementation of filters in Chapter \ref{chap:FilterTypeReference}.
Most of the outward facing interfaces of parallel algorithms in \VTKm are through these filter classes.
For everything else, there is the \vtkmcont{TryExecute} function.
The \textidentifier{Invoker} is internally using a function named \vtkmcont{TryExecute} to choose a device.
This \textidentifier{TryExecute} function can be also be used in other instances where a specific device needs to be chosen.
\textidentifier{TryExecute} is a simple, generic mechanism to run an algorithm that requires a device adapter without directly specifying a device adapter.
\vtkmcont{TryExecute} is a templated function with two arguments.
\vtkmcont{TryExecute} is a templated function.
The first argument is a functor object whose parenthesis operator takes a device adapter tag and returns a \textcode{bool} that is true if the call succeeds on the given device.
\index{runtime device tracker|}\index{device adapter!runtime tracker|}The second argument is a \vtkmcont{RuntimeDeviceTracker} that specifies what devices to try.
\textidentifier{RuntimeDeviceTracker} is documented in Section~\ref{sec:RuntimeDeviceTracker}.
If any further arguments are given to \textidentifier{TryExecute}, they are passed on to the functor.
Thus, the parenthesis operator on the functor should take a device adapter tag as its first argument and any remaining arguments must match those passed to \textidentifier{TryExecute}.
To demonstrate the operation of \textidentifier{TryExecute}, consider an operation to find the average value of an array.
Doing so with a given device adapter is a straightforward use of the reduction operator.
\fix{Change this example to simply the functor by passing InArray and OutValue as operator parameters.}
\vtkmlisting[ex:ArrayAverageImpl]{A function to find the average value of an array in parallel.}{ArrayAverageImpl.cxx}
The function in Example~\ref{ex:ArrayAverageImpl} requires a device adapter.
......@@ -37,12 +35,6 @@ We then create a new version of the array average function that does not need a
\vtkmlisting[ex:ArrayAverageTryExecute]{Using \textidentifier{TryExecute}.}{ArrayAverageTryExecute.cxx}
\begin{didyouknow}
\textidentifier{TryExecute} calls \vtkmcont{TryExecuteOnDevice} internally.
This means that \vtkmcont{GetRuntimeDeviceTracker} is automatically called and ensures execution on devices that have been enabled.
It is up to the application to enable/disable devices before calling \textidentifier{TryExecute} using the methods provided in Section~\ref{sec:RuntimeDeviceTracker}.
\end{didyouknow}
\begin{commonerrors}
When \textidentifier{TryExecute} calls the operation of your functor, it will catch any exceptions that the functor might throw.
\textidentifier{TryExecute} will interpret any thrown exception as a failure on that device and try another device.
......
......@@ -33,7 +33,7 @@ Data pointed to by a \textidentifier{VariantArrayHandle} is not directly
accessible. However, there are a few generic queries you can make without
directly knowing the data type. The \classmember*{VariantArrayHandle}{GetNumberOfValues} method
returns the length of the array with respect to its base data type. It is
also common in VTK-m to use data types, such as \vtkm{Vec}, with multiple
also common in \VTKm to use data types, such as \vtkm{Vec}, with multiple
components per value. The \classmember*{VariantArrayHandle}{GetNumberOfComponents} method returns
the number of components in a vector-like type (or 1 for scalars).
......@@ -51,7 +51,7 @@ with the same underlying type as the original array.
Before the data with a \textidentifier{VariantArrayHandle} can be accessed,
the type of the array must be established. This is usually done
internally within VTK-m when a worklet or filter is invoked and the \textidentifier{VariantArrayHandle}
internally within \VTKm when a worklet or filter is invoked and the \textidentifier{VariantArrayHandle}
is converted into an \textidentifier{ArrayHandleVirtual}.
However, it is also possible to query the types and cast to a concrete
\textidentifier{ArrayHandle}.
......@@ -75,7 +75,7 @@ Chapter~\ref{chap:AccessingAllocatingArrays}. The easiest ways to do this is to
or \classmember*{VariantArrayHandle}{CopyTo} method when wanting a concrete \textidentifier{ArrayHandle}.
The \classmember{VariantArrayHandle}{AsVirtual} templated method takes a
value type as a template parameter and returns a
value type as a template parameter and returns an
array handle virtual that points to the array in \textidentifier{VariantArrayHandle}.
If the given types are incorrect, then \classmember*{VariantArrayHandle}{AsVirtual} throws
a \vtkmcont{ErrorControlBadValue} exception.
......@@ -83,14 +83,6 @@ a \vtkmcont{ErrorControlBadValue} exception.
\vtkmlisting{Casting a \textidentifier{VariantArrayHandle} to a virtual
\textidentifier{ArrayHandle}.}{AsVirtualVariantArrayHandle.cxx}
\begin{commonerrors}
Remember that \textidentifier{ArrayHandle} and
\textidentifier{VariantArrayHandle} represent pointers to the data, so
this ``copy'' is a shallow copy. There is still only one copy of the
data, and if you change the data in one array handle that change is
reflected in the other.
\end{commonerrors}
The \classmember{VariantArrayHandle}{CopyTo} templated method takes a
reference to an \textidentifier{ArrayHandle} as an argument and sets that
array handle to point to the array in \textidentifier{VariantArrayHandle}.
......@@ -140,7 +132,7 @@ appropriate type.
The \classmember*{VariantArrayHandle}{CastAndCall} method can only check a finite number of value types.
The default form of \classmember*{VariantArrayHandle}{CastAndCall} uses a default set of common
types. These default lists can be overridden using the VTK-m list tags
types. These default lists can be overridden using the \VTKm list tags
facility, which is discussed at length in Section~\ref{sec:ListTags}.
Common type lists for value are defined in
......@@ -165,7 +157,7 @@ templated on the type of \textidentifier{VariantArrayHandle}. This is to
accommodate using the objects from the \textcode{ResetTypes} method, which
have the same behavior but different type names.)
So the default component type list contains a subset of the basic VTK-m
So the default component type list contains a subset of the basic \VTKm
types. If you wanted to accommodate more types, you could use
\classmember*{VariantArrayHandle}{ResetTypes}.
......
......@@ -40,36 +40,7 @@ tag names.
\begin{figure}
\centering
\small
\begin{tabular}{@{}c@{ }c@{ }c@{}}
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsVertex}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsLine}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsTriangle}} \\
\vtkm{CELL\_SHAPE\_VERTEX} &
\vtkm{CELL\_SHAPE\_LINE} &
\vtkm{CELL\_SHAPE\_TRIANGLE} \\
\vtkm{CellShapeTagVertex} \index{vertex} &
\vtkm{CellShapeTagLine} \index{line} &
\vtkm{CellShapeTagTriangle} \index{triangle} \\[2ex]
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsPolygon}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsQuadrilateral}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsTetrahedron}} \\
\vtkm{CELL\_SHAPE\_POLYGON} &
\vtkm{CELL\_SHAPE\_QUAD} &
\vtkm{CELL\_SHAPE\_TETRA} \\
\vtkm{CellShapeTagPolygon} \index{polygon} &
\vtkm{CellShapeTagQuad} \index{quadrilateral} &
\vtkm{CellShapeTagTetra} \index{tetrahedron} \\[2ex]
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsHexahedron}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsWedge}} &
\raisebox{-0.5\height}{\includegraphics{images/CellConnectionsPyramid}} \\
\vtkm{CELL\_SHAPE\_HEXAHEDRON} &
\vtkm{CELL\_SHAPE\_WEDGE} &
\vtkm{CELL\_SHAPE\_PYRAMID} \\
\vtkm{CellShapeTagHexahedron} \index{hexahedron} &
\vtkm{CellShapeTagWedge} \index{wedge} &
\vtkm{CellShapeTagPyramid} \index{pyramid}
\end{tabular}
\input{CellShapeFigure}
\caption{Basic Cell Shapes}
\label{fig:CellShapes}
\end{figure}
......
......@@ -4,15 +4,15 @@
\label{chap:TransferringArguments}
\label{chap:WorkletArguments}
From the \controlsignature and \executionsignature defined in worklets, VTK-m uses template meta-programming to build the code required to manage data from control to execution environment.
From the \controlsignature and \executionsignature defined in worklets, VTK-m uses template meta-programming to build the code required to manage data from the control to the execution environment.
These signatures contain tags that define the meaning of each argument and control how the argument data are transferred from the control to execution environments and broken up for each worklet instance.
Chapter \ref{chap:WorkletTypeReference} documents the many \controlsignature and \executionsignature tags that come with the worklet types.
This chapter discusses the internals of these tags and how they control data management.
Defining new worklet argument types can allow you to define new data structures in VTK-m.
Defining new worklet argument types can allow you to define new data structures in \VTKm.
New worklet arguments are also usually a critical components for making new worklet types, as described in Chapter~\ref{chap:NewWorkletTypes}.
The management of data in worklet arguments is handled by three classes that provide type checking, transportation, and fetching.
The management of data in worklet arguments is handled by three classes that provide type checking, transportation, and fetching, respectively.
This chapter will first describe these type checking, transportation, and fetching classes and then describe how \controlsignature and \executionsignature tags specify these classes.
Throughout this chapter we demonstrate the definition of worklet arguments using an example of a worklet argument that represents line segments in 2D.
......@@ -28,7 +28,7 @@ Plus, we will use this special worklet argument for our example of a custom work
\index{type check|(}
Before attempting to move data from the control to the execution
environment, the VTK-m dispatchers check the input types to ensure that
environment, the \VTKm invokers check the input types to ensure that
they are compatible with the associated \controlsignature concept. This is
done with the \vtkmcontarg{TypeCheck} \textcode{struct}.
......@@ -44,7 +44,7 @@ Type checks are implemented with a defined type check tag (which, by
convention, is defined in the \vtkmcontarg{} namespace and starts with
\textcode{TypeCheckTag}) and a partial specialization of the
\vtkmcontarg{TypeCheck} structure. The following type checks (identified by
their tags) are provided in VTK-m.
their tags) are provided in \VTKm.
\begin{description}
\item[\vtkmcontarg{TypeCheckTagExecObject}] \index{type check!execution object}
......@@ -62,21 +62,21 @@ their tags) are provided in VTK-m.
Here are some trivial examples of using
\textidentifier{TypeCheck}. Typically these checks are done internally in
the base VTK-m dispatcher code, so these examples are for demonstration
the base \VTKm invoker code, so these examples are for demonstration
only.
\vtkmlisting{Behavior of \protect\vtkmcontarg{TypeCheck}.}{TypeCheck.cxx}
A type check is created by first defining a type check tag object, which by convention is placed in the \vtkmcontarg{} namespace and whose name starts with \textidentifier{TypeCheckTag}.
Then, create a specialization of the \vtkmcontarg{TypeCheck} template class with the first template argument matching the aforementioned tag.
As stated previously, the \textidentifier{TypeCheck} class must contain a \classmember*{TypeCheck}{value} static constant Boolean representing whether the type is acceptable for the corresponding \textcode{Invoke} argument.
As stated previously, the \textidentifier{TypeCheck} class must contain a \classmember*{TypeCheck}{value} static constant Boolean representing whether the type is acceptable for the corresponding \textidentifier{Invoker} argument.
This example of a \textidentifier{TypeCheck} returns true for control objects that are \textidentifier{ArrayHandle}s with a value type that is a floating point \vtkm{Vec} of size 2.
\vtkmlisting[ex:TypeCheckTag2DCoordinates]{Defining a custom \textidentifier{TypeCheck}.}{TypeCheckImpl.h}
\begin{didyouknow}
The type check defined in Example~\ref{ex:TypeCheckTag2DCoordinates} could actually be replaced by the more general \textidentifier{TypeCheckTagArray} that already comes with \VTKm (and, in fact, the implementation uses this type check internally for simplicity).
The type check defined in Example~\ref{ex:TypeCheckTag2DCoordinates} could actually be replaced by the more general \textidentifier{TypeCheckTagArray} that already comes with \VTKm.
This example is mostly provided for demonstrative purposes.
In practice, it is often useful to use \textcode{std::is\_same} or \textcode{std::is\_base\_of}, which are provided by the standard template library starting with C++11, to determine \classmember*{TypeCheck}{value} in a \textidentifier{TypeCheck}.
\end{didyouknow}
......@@ -89,9 +89,8 @@ This example of a \textidentifier{TypeCheck} returns true for control objects th
\index{transport|(}
After all the argument types are checked, the base dispatcher must load the
data into the execution environment before scheduling a job to run
there. This is done with the \vtkmcontarg{Transport} \textcode{struct}.
After all the argument types are checked, the \VTKm dispatch mechanism must load the data into the execution environment before scheduling a job to run there.
This is done with the \vtkmcontarg{Transport} \textcode{struct}.
The \textidentifier{Transport} \textcode{struct} is templated with three
parameters. The first parameter is a tag that identifies which transport to
......@@ -107,7 +106,7 @@ Transports are implemented with a defined transport tag (which, by
convention, is defined in the \vtkmcontarg{} namespace and starts with
\textcode{TransportTag}) and a partial specialization of the
\vtkmcontarg{Transport} structure. The following transports (identified by
their tags) are provided in VTK-m.
their tags) are provided in \VTKm.
\begin{description}
\item[\vtkmcontarg{TransportTagExecObject}]
......@@ -168,7 +167,7 @@ their tags) are provided in VTK-m.
Here are some trivial examples of using
\textidentifier{Transport}. Typically this movement is done internally in
the base VTK-m dispatcher code, so these examples are for demonstration
the \VTKm dispatching code, so these examples are for demonstration
only.
\vtkmlisting{Behavior of \protect\vtkmcontarg{Transport}.}{Transport.cxx}
......@@ -184,7 +183,7 @@ The resulting execution object is an array portal containing \textidentifier{Vec
\begin{commonerrors}
It is fair to assume that the \textidentifier{Transport}'s control object type matches whatever the associated \textidentifier{TypeCheck} allows.
However, it is good practice to provide a secondary compile-time check in the \textidentifier{Transport} class for debugging purposes in case there is a problem with the \textidentifier{TypeCheck} or this \textidentifier{Transport} is used with an unexpected \textidentifier{TypeCheck}.
However, it is good practice to provide a secondary compile-time check in the \textidentifier{Transport} class, like the one on line \ref{ex:TransportImpl.h:CheckControlObject} in Example \ref{ex:TransportImpl}, for debugging purposes in case there is a problem with the \textidentifier{TypeCheck} or this \textidentifier{Transport} is used with an unexpected \textidentifier{TypeCheck}.
\end{commonerrors}
\index{transport|)}
......@@ -195,7 +194,7 @@ The resulting execution object is an array portal containing \textidentifier{Vec
\index{fetch|(}
Before the function of a worklet is invoked, the VTK-m internals pull the
Before the function of a worklet is invoked, the \VTKm internals pull the
appropriate data out of the execution object and pass it to the worklet
function. A class named \vtkmexecarg{Fetch} is responsible for pulling this
data out and putting computed data in to the execution objects.
......@@ -282,7 +281,7 @@ As stated previously, the \textidentifier{Fetch} class must contain a \classmemb
\end{didyouknow}
In addition to the aforementioned aspect tags that are explicitly paired
with fetch tags, VTK-m also provides some aspect tags that either modify
with fetch tags, \VTKm also provides some aspect tags that either modify
the behavior of a general fetch or simply ignore the type of fetch.
\begin{description}
......@@ -334,7 +333,7 @@ This example creates a specialization of a \textidentifier{Fetch} to retrieve th
\index{control signature!tags|(}
\index{signature!control!tags|(}
The type checks, transports, and fetches defined in the previous sections of this chapter conspire to interpret the arguments given to a dispatcher's \textcode{Invoke} method and provide data to an instance of a worklet.
The type checks, transports, and fetches defined in the previous sections of this chapter conspire to interpret the arguments given to a \textidentifier{Invoker} and provide data to an instance of a worklet.
What remains to be defined are the tags used in the \controlsignature and \executionsignature that bring these three items together.
These two types of tags are defined differently.
In this section we discuss the \controlsignature tags.
......
......@@ -3,7 +3,6 @@
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/PointElevation.h>
#include <vtkm/cont/testing/Testing.h>
......@@ -245,9 +244,8 @@ void GetElevationAirPressure(vtkm::cont::DataSet grid, FooFieldsDeque* fields)
elevation.SetHighPoint(vtkm::make_Vec(0.0, 0.0, 2000.0));
elevation.SetRange(101325.0, 77325.0);
vtkm::worklet::DispatcherMapField<vtkm::worklet::PointElevation> dispatcher(
elevation);
dispatcher.Invoke(grid.GetCoordinateSystem().GetData(), pressureHandle);
vtkm::cont::Invoker invoke;
invoke(elevation, grid.GetCoordinateSystem().GetData(), pressureHandle);
// Make sure the values are flushed back to the control environment.
pressureHandle.SyncControlArray();
......
......@@ -38,7 +38,6 @@ set(example_src
GenerateMeshConstantShape.cxx
GenerateMeshHash.cxx
GenerateMeshVariableShape.cxx
ImplicitFunctions.cxx
Initialization.cxx
IO.cxx
ListTags.cxx
......
......@@ -219,8 +219,14 @@ struct ExtractFaces
// To construct an ArrayHandleGroupVecVariable, we need to convert
// pointsPerFace to an array of offsets
vtkm::Id faceIndicesSize;
vtkm::cont::ArrayHandle<vtkm::Id> faceIndexOffsets =
vtkm::cont::ConvertNumComponentsToOffsets(pointsPerFace, faceIndicesSize);
vtkm::cont::ArrayHandle<vtkm::Id> faceIndexOffsetsExtended =
vtkm::cont::ConvertNumIndicesToOffsets(pointsPerFace, faceIndicesSize);
// faceIndexOffsetsExtended contains the extended scan needed to fill the output
// CellSetExplicit dataset; an exclusive scan is needed to construct a
// ArrayHandleGroupVecVariable
auto faceIndexOffsetsExclusive = vtkm::cont::make_ArrayHandleView(
faceIndexOffsetsExtended, 0, faceIndexOffsetsExtended.GetNumberOfValues() - 1);
// We need to preallocate the array for faceIndices (because that is the
// way ArrayHandleGroupVecVariable works). We use the value previously
......@@ -230,17 +236,16 @@ struct ExtractFaces
// Get the cell index array for all the faces
vtkm::worklet::DispatcherMapTopology<FacesExtract> extractDispatcher(scatter);
extractDispatcher.Invoke(
cellSetIn,
vtkm::cont::make_ArrayHandleGroupVecVariable(faceIndices, faceIndexOffsets));
extractDispatcher.Invoke(cellSetIn,
vtkm::cont::make_ArrayHandleGroupVecVariable(
faceIndices, faceIndexOffsetsExclusive));
// Construct the resulting cell set and return
vtkm::cont::CellSetExplicit<> cellSetOut;
cellSetOut.Fill(cellSetIn.GetNumberOfPoints(),
faceShapes,
pointsPerFace,
faceIndices,
faceIndexOffsets);
faceIndexOffsetsExtended);
return cellSetOut;
}
};
......
......@@ -189,13 +189,12 @@ void CreateExplicitGrid()
// Do a simple check of the connectivity by getting the number of cells
// incident on each point. This array is unlikely to be correct if the
// topology got screwed up.
vtkm::cont::ArrayHandle<vtkm::IdComponent> numCellsPerPoint =
cellSet.GetNumIndicesArray(vtkm::TopologyElementTagPoint(),
vtkm::TopologyElementTagCell());
auto numCellsPerPoint = cellSet.GetNumIndicesArray(vtkm::TopologyElementTagPoint(),
vtkm::TopologyElementTagCell());
vtkm::cont::printSummary_ArrayHandle(numCellsPerPoint, std::cout);
std::cout << std::endl;
vtkm::cont::ArrayHandle<vtkm::IdComponent>::PortalConstControl numCellsPortal =
numCellsPerPoint.GetPortalConstControl();
auto numCellsPortal = numCellsPerPoint.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(numCellsPortal.Get(0), 2),
"Wrong number of cells on point 0");
VTKM_TEST_ASSERT(test_equal(numCellsPortal.Get(1), 2),
......@@ -281,13 +280,12 @@ void CreateExplicitGridIterative()
// Do a simple check of the connectivity by getting the number of cells
// incident on each point. This array is unlikely to be correct if the
// topology got screwed up.
vtkm::cont::ArrayHandle<vtkm::IdComponent> numCellsPerPoint =
cellSet.GetNumIndicesArray(vtkm::TopologyElementTagPoint(),
vtkm::TopologyElementTagCell());
auto numCellsPerPoint = cellSet.GetNumIndicesArray(vtkm::TopologyElementTagPoint(),
vtkm::TopologyElementTagCell());
vtkm::cont::printSummary_ArrayHandle(numCellsPerPoint, std::cout);
std::cout << std::endl;
vtkm::cont::ArrayHandle<vtkm::IdComponent>::PortalConstControl numCellsPortal =
numCellsPerPoint.GetPortalConstControl();
auto numCellsPortal = numCellsPerPoint.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(numCellsPortal.Get(0), 2),
"Wrong number of cells on point 0");
VTKM_TEST_ASSERT(test_equal(numCellsPortal.Get(1), 2),
......
......@@ -122,6 +122,7 @@ struct Transport<vtkm::cont::arg::TransportTag2DLineSegmentsIn,
ContObjectType,
Device>
{
//// LABEL CheckControlObject
VTKM_IS_ARRAY_HANDLE(ContObjectType);
using GroupedArrayType = vtkm::cont::ArrayHandleGroupVec<ContObjectType, 2>;
......
......@@ -4,8 +4,6 @@
#include <vtkm/exec/CellEdge.h>
#include <vtkm/worklet/AverageByKey.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/DispatcherReduceByKey.h>
#include <vtkm/worklet/Keys.h>
#include <vtkm/worklet/ScatterCounting.h>
......@@ -22,178 +20,102 @@ namespace worklet
namespace
{
struct ExtractEdges
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeCount.cxx
////
struct CountEdgesWorklet : vtkm::worklet::WorkletVisitCellsWithPoints
{
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeCount.cxx
////
struct CountEdges : vtkm::worklet::WorkletVisitCellsWithPoints
using ControlSignature = void(CellSetIn cellSet, FieldOut numEdges);
using ExecutionSignature = _2(CellShape, PointCount);
using InputDomain = _1;
template<typename CellShapeTag>
VTKM_EXEC_CONT vtkm::IdComponent operator()(
CellShapeTag cellShape,
vtkm::IdComponent numPointsInCell) const
{
using ControlSignature = void(CellSetIn cellSet, FieldOut numEdges);
using ExecutionSignature = _2(CellShape, PointCount);
using InputDomain = _1;
template<typename CellShapeTag>
VTKM_EXEC_CONT vtkm::IdComponent operator()(
CellShapeTag cellShape,
vtkm::IdComponent numPointsInCell) const
{
return vtkm::exec::CellEdgeNumberOfEdges(numPointsInCell, cellShape, *this);
}
};
////
//// END-EXAMPLE GenerateMeshCombineLikeCount.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeGenIds.cxx
////
class EdgeIds : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellSet, FieldOut canonicalIds);
using ExecutionSignature = void(CellShape cellShape,
PointIndices globalPointIndices,
VisitIndex localEdgeIndex,
_2 canonicalIdOut);
using InputDomain = _1;
using ScatterType = vtkm::worklet::ScatterCounting;
template<typename CellShapeTag, typename PointIndexVecType>
VTKM_EXEC void operator()(CellShapeTag cellShape,
const PointIndexVecType& globalPointIndicesForCell,
vtkm::IdComponent localEdgeIndex,
vtkm::Id2& canonicalIdOut) const
{
vtkm::IdComponent numPointsInCell =
globalPointIndicesForCell.GetNumberOfComponents();
canonicalIdOut = vtkm::exec::CellEdgeCanonicalId(numPointsInCell,
localEdgeIndex,
cellShape,
globalPointIndicesForCell,
*this);
}
};
////
//// END-EXAMPLE GenerateMeshCombineLikeGenIds.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeGenIndices.cxx
////
class EdgeIndices : public vtkm::worklet::WorkletReduceByKey
{
public:
using ControlSignature = void(KeysIn keys,
WholeCellSetIn<> inputCells,
ValuesIn originCells,
ValuesIn originEdges,
ReducedValuesOut connectivityOut);
using ExecutionSignature = void(_2 inputCells,
_3 originCell,
_4 originEdge,
_5 connectivityOut);
using InputDomain = _1;
template<typename CellSetType,
typename OriginCellsType,
typename OriginEdgesType>
VTKM_EXEC void operator()(const CellSetType& cellSet,
const OriginCellsType& originCells,
const OriginEdgesType& originEdges,
vtkm::Id2& connectivityOut) const
{
// Regardless of how many cells/edges are in our local input, we know they are
// all the same, so just pick the first one.
vtkm::IdComponent numPointsInCell = cellSet.GetNumberOfIndices(originCells[0]);
vtkm::IdComponent edgeIndex = originEdges[0];
auto cellShape = cellSet.GetCellShape(originCells[0]);
vtkm::IdComponent pointInCellIndex0 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 0, edgeIndex, cellShape, *this);
vtkm::IdComponent pointInCellIndex1 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 1, edgeIndex, cellShape, *this);
auto globalPointIndicesForCell = cellSet.GetIndices(originCells[0]);
connectivityOut[0] = globalPointIndicesForCell[pointInCellIndex0];
connectivityOut[1] = globalPointIndicesForCell[pointInCellIndex1];
}
};
////
//// END-EXAMPLE GenerateMeshCombineLikeGenIndices.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeInvoke.cxx
////
template<typename CellSetType>
VTKM_CONT vtkm::cont::CellSetSingleType<> Run(const CellSetType& inCellSet)
return vtkm::exec::CellEdgeNumberOfEdges(numPointsInCell, cellShape, *this);
}
};
////
//// END-EXAMPLE GenerateMeshCombineLikeCount.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeGenIds.cxx
////
class EdgeIdsWorklet : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellSet, FieldOut canonicalIds);
using ExecutionSignature = void(CellShape cellShape,
PointIndices globalPointIndices,
VisitIndex localEdgeIndex,
_2 canonicalIdOut);
using InputDomain = _1;
using ScatterType = vtkm::worklet::ScatterCounting;
template<typename CellShapeTag, typename PointIndexVecType>
VTKM_EXEC void operator()(CellShapeTag cellShape,
const PointIndexVecType& globalPointIndicesForCell,
vtkm::IdComponent localEdgeIndex,
vtkm::Id2& canonicalIdOut) const
{
VTKM_IS_DYNAMIC_OR_STATIC_CELL_SET(CellSetType);
// First, count the edges in each cell.
vtkm::cont::ArrayHandle<vtkm::IdComponent> edgeCounts;
vtkm::worklet::DispatcherMapTopology<CountEdges> countEdgeDispatcher;
countEdgeDispatcher.Invoke(inCellSet, edgeCounts);
vtkm::worklet::ScatterCounting scatter(edgeCounts);
this->OutputToInputCellMap =
scatter.GetOutputToInputMap(inCellSet.GetNumberOfCells());
vtkm::worklet::ScatterCounting::VisitArrayType outputToInputEdgeMap =
scatter.GetVisitArray(inCellSet.GetNumberOfCells());
// Second, for each edge, extract a canonical id.
vtkm::cont::ArrayHandle<vtkm::Id2> canonicalIds;
vtkm::worklet::DispatcherMapTopology<EdgeIds> edgeIdsDispatcher(scatter);
edgeIdsDispatcher.Invoke(inCellSet, canonicalIds);
// Third, use a Keys object to combine all like edge ids.
this->CellToEdgeKeys = vtkm::worklet::Keys<vtkm::Id2>(canonicalIds);
// Fourth, use a reduce-by-key to extract indices for each unique edge.
vtkm::cont::ArrayHandle<vtkm::Id> connectivityArray;
vtkm::worklet::DispatcherReduceByKey<EdgeIndices> edgeIndicesDispatcher;
edgeIndicesDispatcher.Invoke(
this->CellToEdgeKeys,
inCellSet,
this->OutputToInputCellMap,
outputToInputEdgeMap,
vtkm::cont::make_ArrayHandleGroupVec<2>(connectivityArray));
// Fifth, use the created connectivity array to build a cell set.
vtkm::cont::CellSetSingleType<> outCellSet;
outCellSet.Fill(
inCellSet.GetNumberOfPoints(), vtkm::CELL_SHAPE_LINE, 2, connectivityArray);
return outCellSet;
vtkm::IdComponent numPointsInCell =
globalPointIndicesForCell.GetNumberOfComponents();
canonicalIdOut = vtkm::exec::CellEdgeCanonicalId(
numPointsInCell, localEdgeIndex, cellShape, globalPointIndicesForCell, *this);
}
////
//// END-EXAMPLE GenerateMeshCombineLikeInvoke.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeMapCellField.cxx
////
template<typename ValueType, typename Storage>
VTKM_CONT vtkm::cont::ArrayHandle<ValueType> ProcessCellField(
const vtkm::cont::ArrayHandle<ValueType, Storage>& inCellField) const
};
////
//// END-EXAMPLE GenerateMeshCombineLikeGenIds.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeGenIndices.cxx
////
class EdgeIndicesWorklet : public vtkm::worklet::WorkletReduceByKey
{
public:
using ControlSignature = void(KeysIn keys,
WholeCellSetIn<> inputCells,
ValuesIn originCells,
ValuesIn originEdges,
ReducedValuesOut connectivityOut);
using ExecutionSignature = void(_2 inputCells,
_3 originCell,
_4 originEdge,
_5 connectivityOut);
using InputDomain = _1;
template<typename CellSetType, typename OriginCellsType, typename OriginEdgesType>
VTKM_EXEC void operator()(const CellSetType& cellSet,
const OriginCellsType& originCells,
const OriginEdgesType& originEdges,
vtkm::Id2& connectivityOut) const
{
return vtkm::worklet::AverageByKey::Run(
this->CellToEdgeKeys,
vtkm::cont::make_ArrayHandlePermutation(this->OutputToInputCellMap,
inCellField));
// Regardless of how many cells are sharing the edge we are generating, we
// know that each cell/edge given to us by the reduce-by-key refers to the
// same edge, so we can just look at the first cell to get the edge.
vtkm::IdComponent numPointsInCell = cellSet.GetNumberOfIndices(originCells[0]);
vtkm::IdComponent edgeIndex = originEdges[0];
auto cellShape = cellSet.GetCellShape(originCells[0]);
vtkm::IdComponent pointInCellIndex0 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 0, edgeIndex, cellShape, *this);
vtkm::IdComponent pointInCellIndex1 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 1, edgeIndex, cellShape, *this);
auto globalPointIndicesForCell = cellSet.GetIndices(originCells[0]);
connectivityOut[0] = globalPointIndicesForCell[pointInCellIndex0];
connectivityOut[1] = globalPointIndicesForCell[pointInCellIndex1];
}
////
//// END-EXAMPLE GenerateMeshCombineLikeMapCellField.cxx
////
vtkm::worklet::ScatterCounting::OutputToInputMapType OutputToInputCellMap;
vtkm::worklet::Keys<vtkm::Id2> CellToEdgeKeys;
};
////
//// END-EXAMPLE GenerateMeshCombineLikeGenIndices.cxx
////
} // anonymous namespace
} // namespace worklet
......@@ -223,7 +145,8 @@ public:
const vtkm::filter::PolicyBase<Policy>& policy);
private:
vtkm::worklet::ExtractEdges Worklet;
vtkm::worklet::ScatterCounting::OutputToInputMapType OutputToInputCellMap;
vtkm::worklet::Keys<vtkm::Id2> CellToEdgeKeys;
};
//// PAUSE-EXAMPLE
......@@ -242,20 +165,54 @@ namespace
{
//// RESUME-EXAMPLE
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeInvoke.cxx
////
template<typename Policy>
inline VTKM_CONT vtkm::cont::DataSet ExtractEdges::DoExecute(
const vtkm::cont::DataSet& inData,
vtkm::filter::PolicyBase<Policy> policy)
{
const vtkm::cont::DynamicCellSet& inCells = inData.GetCellSet();
vtkm::cont::CellSetSingleType<> outCells =
this->Worklet.Run(vtkm::filter::ApplyPolicyCellSet(inCells, policy));
const vtkm::cont::DynamicCellSet& inCellSet =
vtkm::filter::ApplyPolicyCellSet(inData.GetCellSet(), policy);
// First, count the edges in each cell.
vtkm::cont::ArrayHandle<vtkm::IdComponent> edgeCounts;
this->Invoke(vtkm::worklet::CountEdgesWorklet{}, inCellSet, edgeCounts);
// Second, using these counts build a scatter that repeats a cell's visit
// for each edge in the cell.
vtkm::worklet::ScatterCounting scatter(edgeCounts);
this->OutputToInputCellMap =
scatter.GetOutputToInputMap(inCellSet.GetNumberOfCells());
vtkm::worklet::ScatterCounting::VisitArrayType outputToInputEdgeMap =
scatter.GetVisitArray(inCellSet.GetNumberOfCells());
// Third, for each edge, extract a canonical id.
vtkm::cont::ArrayHandle<vtkm::Id2> canonicalIds;
this->Invoke(vtkm::worklet::EdgeIdsWorklet{}, scatter, inCellSet, canonicalIds);
// Fourth, construct a Keys object to combine all like edge ids.
this->CellToEdgeKeys = vtkm::worklet::Keys<vtkm::Id2>(canonicalIds);
// Fifth, use a reduce-by-key to extract indices for each unique edge.
vtkm::cont::ArrayHandle<vtkm::Id> connectivityArray;
//// LABEL InvokeEdgeIndices
this->Invoke(vtkm::worklet::EdgeIndicesWorklet{},
this->CellToEdgeKeys,
inCellSet,
this->OutputToInputCellMap,
outputToInputEdgeMap,
vtkm::cont::make_ArrayHandleGroupVec<2>(connectivityArray));
// Sixth, use the created connectivity array to build a cell set.
vtkm::cont::CellSetSingleType<> outCellSet;
outCellSet.Fill(
inCellSet.GetNumberOfPoints(), vtkm::CELL_SHAPE_LINE, 2, connectivityArray);
vtkm::cont::DataSet outData;
outData.SetCellSet(outCells);
outData.SetCellSet(outCellSet);
for (vtkm::IdComponent coordSystemIndex = 0;
coordSystemIndex < inData.GetNumberOfCoordinateSystems();
......@@ -266,33 +223,46 @@ inline VTKM_CONT vtkm::cont::DataSet ExtractEdges::DoExecute(
return outData;
}
////
//// END-EXAMPLE GenerateMeshCombineLikeInvoke.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshCombineLikeMapCellField.cxx
////
template<typename T, typename StorageType, typename Policy>
inline VTKM_CONT bool ExtractEdges::DoMapField(
vtkm::cont::DataSet& result,
const vtkm::cont::ArrayHandle<T, StorageType>& input,
const vtkm::cont::ArrayHandle<T, StorageType>& inputArray,
const vtkm::filter::FieldMetadata& fieldMeta,
const vtkm::filter::PolicyBase<Policy>&)
{
vtkm::cont::Field output;
vtkm::cont::Field outputField;
if (fieldMeta.IsPointField())
{
output = fieldMeta.AsField(input); // pass through
outputField = fieldMeta.AsField(inputArray); // pass through
}
else if (fieldMeta.IsCellField())
{
output = fieldMeta.AsField(this->Worklet.ProcessCellField(input));
auto outputCellArray =
vtkm::worklet::AverageByKey::Run(this->CellToEdgeKeys,
vtkm::cont::make_ArrayHandlePermutation(
this->OutputToInputCellMap, inputArray));
outputField = fieldMeta.AsField(outputCellArray);
}
else
{
return false;
}
result.AddField(output);
result.AddField(outputField);
return true;
}
////
//// END-EXAMPLE GenerateMeshCombineLikeMapCellField.cxx
////
//// PAUSE-EXAMPLE
} // anonymous namespace
......@@ -323,19 +293,6 @@ void CheckOutput(const vtkm::cont::CellSetSingleType<>& cellSet)
VTKM_TEST_ASSERT(connectivityPortal.Get(43) == 10, "Bad edge index");
}
void TryWorklet()
{
std::cout << std::endl << "Trying calling worklet." << std::endl;
vtkm::cont::DataSet inDataSet =
vtkm::cont::testing::MakeTestDataSet().Make3DExplicitDataSet5();
vtkm::cont::CellSetExplicit<> inCellSet;
inDataSet.GetCellSet().CopyTo(inCellSet);
vtkm::worklet::ExtractEdges worklet;
vtkm::cont::CellSetSingleType<> outCellSet = worklet.Run(inCellSet);
CheckOutput(outCellSet);
}
void TryFilter()
{
std::cout << std::endl << "Trying calling filter." << std::endl;
......@@ -344,9 +301,6 @@ void TryFilter()
vtkm::filter::ExtractEdges filter;
// NOTE 2018-03-21: I expect this to fail in the short term. Right now no fields
// are copied from input to output. The default should be changed to copy them
// all.
vtkm::cont::DataSet outDataSet = filter.Execute(inDataSet);
vtkm::cont::CellSetSingleType<> outCellSet;
......@@ -372,7 +326,6 @@ void TryFilter()
void DoTest()
{
TryWorklet();
TryFilter();
}
......
......@@ -4,7 +4,6 @@
#include <vtkm/exec/CellEdge.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/WorkletMapTopology.h>
......@@ -21,114 +20,60 @@ namespace worklet
namespace
{
struct ExtractEdges
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeCount.cxx
////
struct CountEdgesWorklet : vtkm::worklet::WorkletVisitCellsWithPoints
{
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeCount.cxx
////
struct CountEdges : vtkm::worklet::WorkletVisitCellsWithPoints
using ControlSignature = void(CellSetIn cellSet, FieldOut numEdges);
using ExecutionSignature = _2(CellShape, PointCount);
using InputDomain = _1;
template<typename CellShapeTag>
VTKM_EXEC_CONT vtkm::IdComponent operator()(
CellShapeTag cellShape,
vtkm::IdComponent numPointsInCell) const
{
using ControlSignature = void(CellSetIn cellSet, FieldOut numEdges);
using ExecutionSignature = _2(CellShape, PointCount);
using InputDomain = _1;
template<typename CellShapeTag>
VTKM_EXEC_CONT vtkm::IdComponent operator()(
CellShapeTag cellShape,
vtkm::IdComponent numPointsInCell) const
{
return vtkm::exec::CellEdgeNumberOfEdges(numPointsInCell, cellShape, *this);
}
};
////
//// END-EXAMPLE GenerateMeshConstantShapeCount.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeGenIndices.cxx
////
class EdgeIndices : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellSet, FieldOut connectivityOut);
using ExecutionSignature = void(CellShape, PointIndices, _2, VisitIndex);
using InputDomain = _1;
using ScatterType = vtkm::worklet::ScatterCounting;
template<typename CellShapeTag, typename PointIndexVecType>
VTKM_EXEC void operator()(CellShapeTag cellShape,
const PointIndexVecType& globalPointIndicesForCell,
vtkm::Id2& connectivityOut,
vtkm::IdComponent edgeIndex) const
{
vtkm::IdComponent numPointsInCell =
globalPointIndicesForCell.GetNumberOfComponents();
vtkm::IdComponent pointInCellIndex0 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 0, edgeIndex, cellShape, *this);
vtkm::IdComponent pointInCellIndex1 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 1, edgeIndex, cellShape, *this);
connectivityOut[0] = globalPointIndicesForCell[pointInCellIndex0];
connectivityOut[1] = globalPointIndicesForCell[pointInCellIndex1];
}
};
////
//// END-EXAMPLE GenerateMeshConstantShapeGenIndices.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeInvoke.cxx
////
template<typename CellSetType>
VTKM_CONT vtkm::cont::CellSetSingleType<> Run(const CellSetType& inCellSet)
{
VTKM_IS_DYNAMIC_OR_STATIC_CELL_SET(CellSetType);
vtkm::cont::ArrayHandle<vtkm::IdComponent> edgeCounts;
vtkm::worklet::DispatcherMapTopology<CountEdges> countEdgeDispatcher;
countEdgeDispatcher.Invoke(inCellSet, edgeCounts);
return vtkm::exec::CellEdgeNumberOfEdges(numPointsInCell, cellShape, *this);
}
};
////
//// END-EXAMPLE GenerateMeshConstantShapeCount.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeGenIndices.cxx
////
class EdgeIndicesWorklet : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellSet, FieldOut connectivityOut);
using ExecutionSignature = void(CellShape, PointIndices, _2, VisitIndex);
using InputDomain = _1;
vtkm::worklet::ScatterCounting scatter(edgeCounts);
this->OutputToInputCellMap =
scatter.GetOutputToInputMap(inCellSet.GetNumberOfCells());
using ScatterType = vtkm::worklet::ScatterCounting;
vtkm::cont::ArrayHandle<vtkm::Id> connectivityArray;
vtkm::worklet::DispatcherMapTopology<EdgeIndices> edgeIndicesDispatcher(scatter);
edgeIndicesDispatcher.Invoke(
inCellSet, vtkm::cont::make_ArrayHandleGroupVec<2>(connectivityArray));
template<typename CellShapeTag, typename PointIndexVecType>
VTKM_EXEC void operator()(CellShapeTag cellShape,
const PointIndexVecType& globalPointIndicesForCell,
vtkm::Id2& connectivityOut,
vtkm::IdComponent edgeIndex) const
{
vtkm::IdComponent numPointsInCell =
globalPointIndicesForCell.GetNumberOfComponents();
vtkm::cont::CellSetSingleType<> outCellSet;
outCellSet.Fill(
inCellSet.GetNumberOfPoints(), vtkm::CELL_SHAPE_LINE, 2, connectivityArray);
vtkm::IdComponent pointInCellIndex0 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 0, edgeIndex, cellShape, *this);
vtkm::IdComponent pointInCellIndex1 = vtkm::exec::CellEdgeLocalIndex(
numPointsInCell, 1, edgeIndex, cellShape, *this);
return outCellSet;
}
////
//// END-EXAMPLE GenerateMeshConstantShapeInvoke.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeMapCellField.cxx
////
template<typename ValueType, typename Storage>
VTKM_CONT vtkm::cont::ArrayHandle<ValueType> ProcessCellField(
const vtkm::cont::ArrayHandle<ValueType, Storage>& inCellField) const
{
vtkm::cont::ArrayHandle<ValueType> outCellField;
vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandlePermutation(
this->OutputToInputCellMap, inCellField),
outCellField);
return outCellField;
connectivityOut[0] = globalPointIndicesForCell[pointInCellIndex0];
connectivityOut[1] = globalPointIndicesForCell[pointInCellIndex1];
}
////
//// END-EXAMPLE GenerateMeshConstantShapeMapCellField.cxx
////
private:
vtkm::worklet::ScatterCounting::OutputToInputMapType OutputToInputCellMap;
};
////
//// END-EXAMPLE GenerateMeshConstantShapeGenIndices.cxx
////
} // anonymous namespace
......@@ -159,7 +104,7 @@ public:
const vtkm::filter::PolicyBase<Policy>& policy);
private:
vtkm::worklet::ExtractEdges Worklet;
vtkm::worklet::ScatterCounting::OutputToInputMapType OutputToInputCellMap;
};
//// PAUSE-EXAMPLE
......@@ -178,20 +123,45 @@ namespace
{
//// RESUME-EXAMPLE
// TODO: It would be nice if there was a simpler example of DoExecute.
////
//// BEGIN-EXAMPLE ExtractEdgesFilterDoExecute.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeInvoke.cxx
////
template<typename Policy>
inline VTKM_CONT vtkm::cont::DataSet ExtractEdges::DoExecute(
const vtkm::cont::DataSet& inData,
vtkm::filter::PolicyBase<Policy> policy)
{
const vtkm::cont::DynamicCellSet& inCells = inData.GetCellSet();
const vtkm::cont::DynamicCellSet& inCellSet =
vtkm::filter::ApplyPolicyCellSet(inData.GetCellSet(), policy);
// Count number of edges in each cell.
vtkm::cont::ArrayHandle<vtkm::IdComponent> edgeCounts;
this->Invoke(vtkm::worklet::CountEdgesWorklet{}, inCellSet, edgeCounts);
vtkm::cont::CellSetSingleType<> outCells =
this->Worklet.Run(vtkm::filter::ApplyPolicyCellSet(inCells, policy));
// Build the scatter object (for non 1-to-1 mapping of input to output)
vtkm::worklet::ScatterCounting scatter(edgeCounts);
this->OutputToInputCellMap =
scatter.GetOutputToInputMap(inCellSet.GetNumberOfCells());
vtkm::cont::ArrayHandle<vtkm::Id> connectivityArray;
//// LABEL InvokeEdgeIndices
this->Invoke(vtkm::worklet::EdgeIndicesWorklet{},
scatter,
inCellSet,
vtkm::cont::make_ArrayHandleGroupVec<2>(connectivityArray));
vtkm::cont::CellSetSingleType<> outCellSet;
outCellSet.Fill(
inCellSet.GetNumberOfPoints(), vtkm::CELL_SHAPE_LINE, 2, connectivityArray);
vtkm::cont::DataSet outData;
outData.SetCellSet(outCells);
outData.SetCellSet(outCellSet);
for (vtkm::IdComponent coordSystemIndex = 0;
coordSystemIndex < inData.GetNumberOfCoordinateSystems();
......@@ -202,33 +172,55 @@ inline VTKM_CONT vtkm::cont::DataSet ExtractEdges::DoExecute(
return outData;
}
////
//// END-EXAMPLE GenerateMeshConstantShapeInvoke.cxx
////
////
//// END-EXAMPLE ExtractEdgesFilterDoExecute.cxx
////
////
//// BEGIN-EXAMPLE ExtractEdgesFilterDoMapField.cxx
////
////
//// BEGIN-EXAMPLE GenerateMeshConstantShapeMapCellField.cxx
////
template<typename T, typename StorageType, typename Policy>
inline VTKM_CONT bool ExtractEdges::DoMapField(
vtkm::cont::DataSet& result,
const vtkm::cont::ArrayHandle<T, StorageType>& input,
const vtkm::cont::ArrayHandle<T, StorageType>& inputArray,
const vtkm::filter::FieldMetadata& fieldMeta,
const vtkm::filter::PolicyBase<Policy>&)
{
vtkm::cont::Field output;
vtkm::cont::Field outputField;
if (fieldMeta.IsPointField())
{
output = fieldMeta.AsField(input); // pass through
outputField = fieldMeta.AsField(inputArray); // pass through
}
else if (fieldMeta.IsCellField())
{
output = fieldMeta.AsField(this->Worklet.ProcessCellField(input));
vtkm::cont::ArrayHandle<T> outputCellArray;
vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandlePermutation(
this->OutputToInputCellMap, inputArray),
outputCellArray);
outputField = fieldMeta.AsField(outputCellArray);
}
else
{
return false;
}
result.AddField(output);
result.AddField(outputField);
return true;
}
////
//// END-EXAMPLE GenerateMeshConstantShapeMapCellField.cxx
////
////
//// END-EXAMPLE ExtractEdgesFilterDoMapField.cxx
////
//// PAUSE-EXAMPLE
} // anonymous namespace
......@@ -260,19 +252,6 @@ void CheckOutput(const vtkm::cont::CellSetSingleType<>& cellSet)
VTKM_TEST_ASSERT(connectivityPortal.Get(69) == 10, "Bad edge index");
}
void TryWorklet()
{
std::cout << std::endl << "Trying calling worklet." << std::endl;
vtkm::cont::DataSet inDataSet =
vtkm::cont::testing::MakeTestDataSet().Make3DExplicitDataSet5();
vtkm::cont::CellSetExplicit<> inCellSet;
inDataSet.GetCellSet().CopyTo(inCellSet);
vtkm::worklet::ExtractEdges worklet;
vtkm::cont::CellSetSingleType<> outCellSet = worklet.Run(inCellSet);
CheckOutput(outCellSet);
}
void TryFilter()
{
std::cout << std::endl << "Trying calling filter." << std::endl;
......@@ -281,9 +260,6 @@ void TryFilter()
vtkm::filter::ExtractEdges filter;
// NOTE 2018-03-21: I expect this to fail in the short term. Right now no fields
// are copied from input to output. The default should be changed to copy them
// all.
vtkm::cont::DataSet outDataSet = filter.Execute(inDataSet);
vtkm::cont::CellSetSingleType<> outCellSet;
......@@ -309,7 +285,6 @@ void TryFilter()
void DoTest()
{
TryWorklet();
TryFilter();
}
......