Commit 70701745 authored by Andrew Bauer's avatar Andrew Bauer

Improvements for Catalyst UG.

Using minted for code sections, improved a couple of images and
other general fixes.
parent 3f153170
......@@ -7,36 +7,34 @@ references. The latter two classes keep track of other vtkObjects by managing th
reference count. When these objects are created, they increment the reference count of the
object they are referring to and when they go out of scope, they decrement the reference count
of the object they are referring to. The following example demonstrates this.
\begin{cpplst}
\begin{minted}{c++}
{
vtkNew<vtkDoubleArray> a; // a's ref count = 1
a->Setame(``an array'');
vtkNew<vtkDoubleArray> a; // a's ref count = 1
a->Setame("an array");
vtkSmartPointer<vtkPointData> pd =
vtkSmartPointer<vtkPointData>::New(); // pd's ref count = 1
pd->AddArray(a.GetPointer()); // a's ref count = 2
vtkSmartPointer<vtkPointData>::New(); // pd's ref count = 1
pd->AddArray(a.GetPointer()); // a's ref count = 2
vtkSmartPointer<vtkDoubleArray> a2 =
vtkSmartPointer<vtkDoubleArray>::New(); // a2's ref count = 1
pd->AddArray(a2); // a2's ref count = 2
pd->AddArray(a2); // a2's ref count = 2
vtkWeakPointer<vtkPointData> pd2;
pd2 = pd; // pd's ref count = 1
pd2 = pd; // pd's ref count = 1
vtkPointData* pd3 = vtkPointData::New();
pd2 = pd3;
pd3->Delete(); // pd3 is deleted
pd2->GetClassName(); // bug!
} // don't need to call Delete on any object
\end{cpplst}
pd3->Delete(); // pd3 is deleted
pd2->GetClassName(); // bug!
} // don't need to call Delete on any object
\end{minted}
Note that when passing a pointer returned from vtkNew as a parameter to a method that the
GetPointer() method must be used. Other than this caveat, vtkSmartPointer and vtkNew objects
can be treated as pointers.
\subsection{ParaView Catalyst for Outputting the Full Dataset}\label{appendix:gridwriterscript}
\subsection{ParaView Catalyst Python Script for Outputting the Full Dataset}\label{appendix:gridwriterscript}
The following script will write out the full dataset every time step for the ``input'' grid provided by
the adaptor to Catalyst. Change ``input'' on line 7 to the appropriate identifier
for adaptors that provide multiple grids. Note that this file is available at \url{https://github.com/Kitware/ParaViewCatalystExampleCode/blob/master/SampleScripts/gridwriter.py}.
\begin{python}
\begin{minted}{python}
from paraview.simple import *
from paraview import coprocessing
......@@ -45,20 +43,27 @@ def CreateCoProcessor():
class Pipeline:
adaptorinput = coprocessor.CreateProducer( datadescription, "input" )
grid = adaptorinput.GetClientSideObject().GetOutputDataObject(0)
if grid.IsA('vtkImageData') or grid.IsA('vtkUniformGrid'):
writer = coprocessor.CreateWriter( XMLPImageDataWriter, "filename_%t.pvti", 1 )
elif grid.IsA('vtkRectilinearGrid'):
writer = coprocessor.CreateWriter( XMLPRectilinearGridWriter, "filename_%t.pvtr", 1 )
elif grid.IsA('vtkStructuredGrid'):
writer = coprocessor.CreateWriter( XMLPStructuredGridWriter, "filename_%t.pvts", 1 )
elif grid.IsA('vtkPolyData'):
writer = coprocessor.CreateWriter( XMLPPolyDataWriter, "filename_%t.pvtp", 1 )
elif grid.IsA('vtkUnstructuredGrid'):
writer = coprocessor.CreateWriter( XMLPUnstructuredGridWriter, "filename_%t.pvtu", 1 )
elif grid.IsA('vtkUniformGridAMR'):
writer = coprocessor.CreateWriter( XMLHierarchicalBoxDataWriter, "filename_%t.vthb", 1 )
elif grid.IsA('vtkMultiBlockDataSet'):
writer = coprocessor.CreateWriter( XMLMultiBlockDataWriter, "filename_%t.vtm", 1 )
if grid.IsA("vtkImageData") or grid.IsA("vtkUniformGrid"):
writer = coprocessor.CreateWriter( XMLPImageDataWriter, \
"filename_%t.pvti", 1 )
elif grid.IsA("vtkRectilinearGrid"):
writer = coprocessor.CreateWriter( XMLPRectilinearGridWriter, \
"filename_%t.pvtr", 1 )
elif grid.IsA("vtkStructuredGrid"):
writer = coprocessor.CreateWriter( XMLPStructuredGridWriter, \
"filename_%t.pvts", 1 )
elif grid.IsA("vtkPolyData"):
writer = coprocessor.CreateWriter( XMLPPolyDataWriter, \
"filename_%t.pvtp", 1 )
elif grid.IsA("vtkUnstructuredGrid"):
writer = coprocessor.CreateWriter( XMLPUnstructuredGridWriter, \
"filename_%t.pvtu", 1 )
elif grid.IsA("vtkUniformGridAMR"):
writer = coprocessor.CreateWriter( XMLHierarchicalBoxDataWriter, \
"filename_%t.vthb", 1 )
elif grid.IsA("vtkMultiBlockDataSet"):
writer = coprocessor.CreateWriter( XMLMultiBlockDataWriter, \
"filename_%t.vtm", 1 )
else:
print "Don't know how to create a writer for a ", grid.GetClassName()
......@@ -69,7 +74,7 @@ def CreateCoProcessor():
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [1]}
freqs = {"input": [1]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
......@@ -91,8 +96,8 @@ def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
coprocessor.UpdateProducers(datadescription)
coprocessor.WriteData(datadescription);
\end{python}
coprocessor.WriteData(datadescription)
\end{minted}
\subsection{Reusing Simulation Memory for Non-VTK Compliant Memory Layouts}\label{appendix:alternatememorylayout}
Recent work in VTK has added the ability to reuse the simulation's memory and data structures
......@@ -122,11 +127,10 @@ is set by numTuples. Finally, if save is set to false then the object will delet
using the delete [] method on each component array when it is done with the memory. Otherwise it
assumes that some other code will de-allocate that memory. The following code snippet demonstrates
its use.
\begin{cpplst}
\begin{minted}{c++}
vtkCPExodusIIResultsArrayTemplate<double>* vtkarray =
vtkCPExodusIIResultsArrayTemplate<double>::New();
vtkarray->SetName(``velocity'');
vtkarray->SetName("velocity");
std::vector<double*> simulationarrays;
simulationarrays.push_back(xvelocity);
simulationarrays.push_back(yvelocity);
......@@ -134,8 +138,7 @@ simulationarrays.push_back(zvelocity);
vtkarray->SetExodusScalarArrays(myarrays, grid->GetNumberOfPoints(), true);
grid->GetPointData()->AddArray(vtkarray);
vtkarray->Delete();
\end{cpplst}
\end{minted}
If the vtkCPExodusIIResultsArrayTemplate class is not appropriate for mapping
simulation memory to VTK memory, a class that derives from vtkMappedDataArray
......@@ -156,7 +159,7 @@ Scalar is the templated data type):
\item vtkIdType LookupTypedValue(Scalar value)
\item void LookupTypedValue(Scalar value, vtkIdList *ids)
\item Scalar GetValue(vtkIdType idx)
\item Scalar& GetValueReference(vtkIdType idx)
\item Scalar\& GetValueReference(vtkIdType idx)
\item void GetTupleValue(vtkIdType idx, Scalar *t)
\end{itemize}
......
......@@ -22,17 +22,17 @@ There can be many editions of Catalyst and these editions can be combined to cre
customized Catalyst builds. Assuming that the desired editions have already been created, the
second step is automated and is done by invoking the following command from the
\textless ParaView\_source\_dir\textgreater /Catalyst directory:
\begin{cpplst}
\begin{minted}{python}
python catalyze.py -i <edition_dir> -o <Catalyst_source_dir>
\end{cpplst}
\end{minted}
Note that more editions can be added with the -i \textless edition\_dir\textgreater and that these are processed in
the order they are given, first to last. For the minimal base edition included with ParaView, this
would be -i Editions/Base. The generated Catalyst source tree will be put in
\textless Catalyst\_source\_dir\textgreater. For configuring Catalyst from the desired build directory, do the
following:
\begin{cpplst}
\begin{minted}{python}
<Catalyst_source_dir>/cmake.sh <Catalyst_source_dir>
\end{cpplst}
\end{minted}
The next step is to build Catalyst (e.g. using make on Linux systems).
\subsection{Creating a ParaView Catalyst Edition}
The main operations for creating an edition of Catalyst are:
......@@ -50,18 +50,22 @@ information with a Python script called catalyze.py that is located in the
By default, Catalyst will be built with the default ParaView build parameters (e.g. build with
shared libraries) unless one of the Catalyst editions changes that in its manifest.json file. An
example of this is shown below:
\begin{python}
``cmake'':{
``cache'':[
\begin{minted}{json}
{
``name'':``BUILD_SHARED_LIBS'',
``type'':``BOOL'',
``value'':``OFF''
"edition": "Custom",
"cmake":{
"cache":[
{
"name":"BUILD_SHARED_LIBS",
"type":"BOOL",
"value":"OFF"
}
]
}
}
]
}
\end{python}
Here, ParaView's CMake option of building shared libraries will be set to OFF. It should be
\end{minted}
Here, ParaView's CMake option of building shared libraries will be set to OFF for this edtion
named Custom. It should be
noted that users can still change the build configuration from these settings but it should be
done after Catalyst is configured with the cmake.sh script.
\subsection{Copying Files from the ParaView Source Tree into the Created Catalyst Source Tree}
......@@ -71,36 +75,41 @@ Catalyst source tree. Most of these files will be filters but there may also be
classes that are needed to be copied over as well. In the following JSON snippet we
demonstrate how to copy the vtkPVArrayCalculator class into the generated Catalyst source
tree.
\begin{python}
``modules'':[
{
``name'':``vtkPVVTKExtensionsDefault'',
``path'':``ParaViewCore/VTKExtensions/Default''
``include'':[
{
``path'':``vtkPVArrayCalculator.cxx''
},
\begin{minted}{json}
{
``path'':``vtkPVArrayCalculator.h''
"edition": "Custom",
"modules":[
{
"name":"vtkPVVTKExtensionsDefault",
"path":"ParaViewCore/VTKExtensions/Default"
"include":[
{
"path":"vtkPVArrayCalculator.cxx"
},
{
"path":"vtkPVArrayCalculator.h"
}
],
"cswrap":true
}
]
}
],
``cswrap'':true
}
}
]
\end{python}
\end{minted}
A description of the pertinent information follows:
\begin{itemize}
\item "name":"vtkPVVTKExtensionsDefault" – the name of the VTK or ParaView module.
\item {\ttfamily "}name{\ttfamily "}:{\ttfamily "}vtkPVVTKExtensionsDefault{\ttfamily "} -- the name of the VTK or ParaView module.
In this case it is vtkPVVTKExtensionsDefault. The name of the module can be found
in the modules.cmake file in the corresponding directory. It is the first argument to the
vtk\_module() function.
\item "path":"ParaViewCore/VTKExtensions/Default" – the subdirectory location of the
module relative to the main ParaView source tree directory (e.g.
\textless ParaView\_source\_dir\textgreater/ParaViewCore/VTKExtensions/Default in this case)
\item ``path'':``vtkPVArrayCalculator.cxx'' – the name of the file to copy from the ParaView
\item {\ttfamily "}path{\ttfamily "}:{\ttfamily "}ParaViewCore/VTKExtensions/Default{\ttfamily "} --
the subdirectory location of the
module relative to the main source tree directory (e.g.
\textless ParaView\_source\_dir\textgreater/ParaViewCore/VTKExtensions/Default
in this case)
\item {\ttfamily "}path{\ttfamily "}:{\ttfamily "}vtkPVArrayCalculator.cxx{\ttfamily "} --
the name of the file to copy from the ParaView
source tree to the generated Catalyst source tree.
\item ``cswrap'':true – if the source code needs to be client-server wrapped such that it is
\item {\ttfamily "}cswrap{\ttfamily "}:true -- if the source code needs to be client-server wrapped such that it is
available through ParaView's server-manager. For filters that are used through
ParaView's Python interface or through a server-manager hard-coded \Cplusplus pipeline
this should be true. For helper classes this should be false.
......@@ -127,75 +136,75 @@ file in the same directory. This is done with the “replace” keyword. An exam
below for the vtkFiltersCore module. Here, the vtkArrayCalculator source code is added to the
Catalyst source tree and so the CMakeLists.txt file in that directory needs to be modified in
order to include that class to be added to the build.
\begin{python}
``modules'':[
\begin{minted}{json}
"modules":[
{
``name'':``vtkFiltersCore'',
``path'':``VTK/Filters/Core'',
``include'':[
"name":"vtkFiltersCore",
"path":"VTK/Filters/Core",
"include":[
{
``path'':``vtkArrayCalculator.cxx''
"path":"vtkArrayCalculator.cxx"
},
{
``path'':``vtkArrayCalculator.h''
"path":"vtkArrayCalculator.h"
}
],
``replace'':[
"replace":[
{
``path'':''VTK/Filters/Core/CMakeLists.txt''
"path":"VTK/Filters/Core/CMakeLists.txt"
}
],
``cswrap'':true
"cswrap":true
}
]
\end{python}
\end{minted}
In this case, the CMakeLists.txt file that needs to be copied to the Catalyst source tree exists in
the \textless edition\_dir\textgreater/VTK/Filters/Core directory, where edition\_dir is the location of this custom
edition of Catalyst. Since the Base edition already includes some files from this directory, we
want to make sure that the CMakeLists.txt file from this edition also includes those from the
Base edition. This CMakeLists.txt file is shown below:
\begin{python}
\begin{minted}{cmake}
set(Module_SRCS
vtkArrayCalculator.cxx
vtkCellDataToPointData.cxx
vtkContourFilter.cxx
vtkContourGrid.cxx
vtkContourHelper.cxx
vtkCutter.cxx
vtkExecutionTimer.cxx
vtkFeatureEdges.cxx
vtkGridSynchronizedTemplates3D.cxx
vtkMarchingCubes.cxx
vtkMarchingSquares.cxx
vtkPointDataToCellData.cxx
vtkPolyDataNormals.cxx
vtkProbeFilter.cxx
vtkQuadricClustering.cxx
vtkRectilinearSynchronizedTemplates.cxx
vtkSynchronizedTemplates2D.cxx
vtkSynchronizedTemplates3D.cxx
vtkSynchronizedTemplatesCutter3D.cxx
vtkThreshold.cxx
vtkAppendCompositeDataLeaves.cxx
vtkAppendFilter.cxx
vtkAppendPolyData.cxx
vtkImageAppend.cxx
vtkArrayCalculator.cxx
vtkCellDataToPointData.cxx
vtkContourFilter.cxx
vtkContourGrid.cxx
vtkContourHelper.cxx
vtkCutter.cxx
vtkExecutionTimer.cxx
vtkFeatureEdges.cxx
vtkGridSynchronizedTemplates3D.cxx
vtkMarchingCubes.cxx
vtkMarchingSquares.cxx
vtkPointDataToCellData.cxx
vtkPolyDataNormals.cxx
vtkProbeFilter.cxx
vtkQuadricClustering.cxx
vtkRectilinearSynchronizedTemplates.cxx
vtkSynchronizedTemplates2D.cxx
vtkSynchronizedTemplates3D.cxx
vtkSynchronizedTemplatesCutter3D.cxx
vtkThreshold.cxx
vtkAppendCompositeDataLeaves.cxx
vtkAppendFilter.cxx
vtkAppendPolyData.cxx
vtkImageAppend.cxx
)
set_source_files_properties(
vtkContourHelper
WRAP_EXCLUDE
vtkContourHelper
WRAP_EXCLUDE
)
vtk_module_library(vtkFiltersCore ${Module_SRCS})
\end{python}
\end{minted}
Note that this CMakeLists.txt file does two things. Firstly it specifies which files to be compiled in
the source directory. Next, it specifies properties of the source files. In the above example,
vtkContourHelper is given a property specifying that it should not be wrapped. Another property
which is commonly set indicates that a class is an abstract class (i.e. it has pure virtual
functions). An example of how to do this is shown below.
\begin{python}
\begin{minted}{cmake}
set_source_files_properties(
vtkXMLPStructuredDataWriter
vtkXMLStructuredDataWriter
ABSTRACT
vtkXMLPStructuredDataWriter
vtkXMLStructuredDataWriter
ABSTRACT
)
\end{python}
\end{minted}
include(UseLATEX)
message("dirs are ${CMAKE_CURRENT_SOURCE_DIR} tooo ${CMAKE_CURRENT_BINARY_DIR}")
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../ParaView/menukeys.sty DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
add_latex_document(
ParaViewCatalystUsersGuide.tex
INPUTS
......
......@@ -34,8 +34,7 @@ properly clean up after itself. For codes that depend on MPI, this is normally d
MPI\_Finalize() is called. The finalize method is often implemented in the adaptor.
\end{enumerate}
This is demonstrated in the code below:
\begin{cpplst} % acbauer -- this didn't look nice with c++
\begin{minted}{c++}
MPI_Init(argc, argv);
#ifdef CATALYST
CatalystInit(argc, argv);
......@@ -51,8 +50,7 @@ for(int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
CatalystFinalize();
#endif
MPI_Finalize();
\end{cpplst}
\end{minted}
The adaptor code should be implemented in a separate source file. The reason for this is that it
simplifies the simulation code build process. The fact that there are only three calls to the
......@@ -68,8 +66,8 @@ responsibilities of the adaptor are:
\end{itemize}
The pseudo-code shown below gives an idea of what this would look like in the adaptor:
\begin{cpplst}
\begin{mdframed}[linecolor=black, topline=true, bottomline=true, backgroundcolor=lbcolor, leftline=true, rightline=true, userdefinedwidth=\textwidth]
\begin{verbatim}
void CatalystCoProcess(int timeStep, double time, <grid info>, <field_info>)
{
1. Specify current timeStep and time for Catalyst
......@@ -80,12 +78,11 @@ void CatalystCoProcess(int timeStep, double time, <grid info>, <field_info>)
6. Specify VTK grid for Catalyst
7. Call Catalyst to perform co-processing (i.e. execute VTK pipelines)
}
\end{cpplst}
\end{verbatim}
\end{mdframed}
A complete example of a simple adaptor is shown below. Following this section we'll discuss the details of the API to help solidify the understanding of the flow of information.
\begin{cpplst}
\begin{minted}{c++}
vtkCPProcessor* Processor = NULL; // static data
void CatalystInit(int numScripts, char* scripts[])
{
......@@ -115,13 +112,12 @@ void CatalystFinalize()
// The grid is a uniform, rectilinear grid that can be specified
// with the number of points in each direction and the uniform
// spacing between points. There is only one field called
// temperature which is specified over the points/nodes of the
// grid.
// temperature which is specified over the points/nodes of the grid.
void CatalystCoProcess(int timeStep, double time, unsigned int numPoints[3],
double spacing[3], double* field)
{
vtkCPDataDescription* dataDescription = vtkCPDataDescription::New();
dataDescription->AddInput(``input'');
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(Processor->RequestDataDescription(dataDescription) != 0)
{
......@@ -129,11 +125,11 @@ void CatalystCoProcess(int timeStep, double time, unsigned int numPoints[3],
// Create an axis-aligned, uniform grid
vtkImageData* grid = vtkImageData::New();
grid->SetExtents(0, numPoints[0]-1, 0, numPoints[1]-1, 0, numPoints[2]-1);
dataDescription->GetInputDescriptionByName(``input'')->SetGrid(grid);
dataDescription->GetInputDescriptionByName("input")->SetGrid(grid);
grid->Delete();
// Create a field associated with points
vtkDoubleArray* array = vtkDoubleArray::New();
array->SetName(``temperature'');
array->SetName("temperature");
array->SetArray(field, grid->GetNumberOfPoints(), 1);
grid->GetPointData()->AddArray(array);
array->Delete();
......@@ -141,9 +137,7 @@ void CatalystCoProcess(int timeStep, double time, unsigned int numPoints[3],
}
dataDescription->Delete();
}
\end{cpplst}
\end{minted}
\section{Overview}
Before we go into the details of the VTK and Catalyst API required for writing the adaptor code, we would like to highlight some general details to keep in mind:
......@@ -196,7 +190,7 @@ This is shown in Figure~\ref{fig:datasetmembers}.
\section{VTK Data Object API}
It is important to know that VTK uses a pipeline architecture to process data.
See the Pipeline section of the ParaView User's Guide
(\url{http://www.paraview.org/Wiki/ParaView/Users_Guide/Table_Of_Contents}).
(\url{www.paraview.org/files/v4.0/ParaViewManual.v4.0.pdf}). % acbauer -- update to the 4.3 PV UG
This pipeline architecture has some consequences for writing the adaptor.
The first is that the objects that process the data, filters in VTK parlance,
are not allowed to modify any input data objects. The second is since VTK is a visualization system,
......@@ -217,16 +211,14 @@ Instead, the reference count is reduced on an instance by invoking the Delete()
needed within a particular scope. Thus the vtkObject (and its
subclasses) will automatically be deleted when the reference count goes to zero. The following code
snippet shows an example of how VTK objects are created, referenced and deleted.
\begin{cpplst}
\begin{minted}{c++}
vtkDoubleArray* a = vtkDoubleArray::New(); // a's ref count = 1
vtkPointData* pd = vtkPointData::New(); // pd's ref count = 1
pd->AddArray(a); // a's ref count = 2
a->Delete(); // a's ref count = 1
a->SetName(``an array''); // valid as a hasn't been deleted
a->SetName("an array"); // valid as a hasn't been deleted
pd->Delete(); // deletes both pd and a
\end{cpplst}
\end{minted}
Some key points here; dereferencing \textit{a} or \textit{pd} after \textit{pd} has been deleted is a bug.
It is valid though to dereference a pointer to a VTK object after \textit{Delete()} has been called
on it as long as its reference count is one or greater. To simplify the management of objects that
......@@ -271,19 +263,17 @@ The parameters are:
\item array -- the pointer to the existing chunk of memory to be used.
\item size -- the length of the array which needs to be at least the number of tuples multiplied by the number of components in the vtkDataArray.
\item save -- set to 1 to keep the object from deleting the memory when it is deleted or set to 0 to have the memory deleted when the object is deleted. By default the memory will be freed using free().
\item deleteMethod -- set to VTK\_DATA\_ARRAY\_FREE to use free() or set to VTK\_DATA\_ARRAY\_DELETE to use delete[] to free the memory.
\item deleteMethod -- set to VTK\_DATA\_ARRAY\_FREE to use free() or to VTK\_DATA\_ARRAY\_DELETE to use delete[] to free the memory.
\end{itemize}
As VTK filters don't modify their input, it is guaranteed that Catalyst will not modify any of the
values in the passed in array. An example of creating a vtkFloatArray from existing memory is shown below:
\begin{cpplst}
\begin{minted}{c++}
vtkFloatArray* arr = vtkFloatArray::New();
arr->SetName(``an array'');
arr->SetName("an array");
float* values = new float[300];
arr->SetArray(values, 300, 0, vtkDoubleArray::VTK_DATA_ARRAY_DELETE);
arr->SetNumberOfComponents(3);
\end{cpplst}
\end{minted}
In this example, values will be deleted when the array arr gets deleted. It will have 100 tuples and 3 components.
The component values still need to be specified in this example however.
......@@ -298,12 +288,11 @@ the following, assuming we're using a vtkFloatArray object:
\end{itemize}
It is important to note that the above methods do not perform range checking. This enables faster execution
time but at the expense of potential memory corruption. Some sample code is shown below.
\begin{cpplst}
\begin{minted}{c++}
vtkIntArray* arr = vtkIntArray::New();
arr->SetNumberOfComponents(3);
arr->SetNumberOfTuples(100);
arr->SetName(``an array'');
arr->SetName("an array");
int tuple[3];
for(vtkIdType i=0;i<100;i++)
{
......@@ -312,8 +301,7 @@ for(vtkIdType i=0;i<100;i++)
tuple[2] = <value>;
arr->SetTupleValue(i, tuple);
}
\end{cpplst}
\end{minted}
If the array length isn't know ahead of time then the following methods, which perform range checking
and allocate memory as necessary, should be used, again assuming that the object is a vtkFloatArray:
\begin{itemize}
......@@ -417,14 +405,12 @@ The origin of the grid can be set as:
\end{itemize}
This is for logical coordinate (0,0,0), even if the whole extent does not contain (0,0,0). This is all
that is required to set the geometry and topology of a vtkImageData object. The example below shows how to create a vtkImageData:
\begin{cpplst}
\begin{minted}{c++}
vtkImageData* grid = vtkImageData::New();
grid->SetExtent(-10, 10, -20, 20, -10, 10);
grid->SetSpacing(2.0, 1.0, 2.0);
grid->SetOrigin(100., 100., 100.);
\end{cpplst}
\end{minted}
In this example, the grid will have 18,081 points and 16,000 cells. The bounds of grid will be 80$\leq$x,y,z$\leq$120.
If blanking is needed then the following vtkUniformGrid methods can be used for blanking points and/or cells:
\begin{itemize}
......@@ -456,8 +442,7 @@ vtkDataArray. This is done with the following methods:
Note that the number of components in each of these arrays should be 1 and the length should
be equal to the local process's extent in that direction, not the whole extent. An example of
how to construct a rectilinear grid is:
\begin{cpplst}
\begin{minted}{c++}
vtkRectilinearGrid* grid = vtkRectilinearGrid::New();
grid->SetExtent(0, 10, 0, 20, 0, 0);
vtkFloatArray* xCoords = vtkFloatArray::New();
......@@ -476,8 +461,7 @@ grid->SetXCoordinates(xCoords);
xCoords->Delete();
grid->SetYCoordinates(yCoords);
yCoords->Delete();
\end{cpplst}
\end{minted}
In this example, the grid has 231 points and 200 2D cells. The points are irregularly spaced in both the X and Y directions.
Since the grid only has one layer of points in the z direction the z coordinates array does not need to
be set explicitly. This results in having the grid lie in the z=0 plane.
......@@ -522,8 +506,7 @@ vtkStructuredGrid is still a topologically regular grid but is geometrically irr
the major functions for creating a vtkStructuredGrid have been discussed already. The only thing
left to mention is that the ordering of the coordinates in vtkPoints must match the ordering that
they are iterated through. This was shown in the figure above. An example of creating a structured grid is:
\begin{cpplst}
\begin{minted}{c++}
vtkStructuredGrid* grid = vtkStructuredGrid::New();
grid->SetExtent(0, 10, 0, 20, 0, 0);
vtkPoints* points = vtkPoints::New();
......@@ -537,8 +520,7 @@ for(int j=0;j<21;j++)
}
grid->SetPoints(points);
points->Delete();
\end{cpplst}
\end{minted}
As mentioned earlier, vtkStructuredGrid also supports blanking. This functionality uses the same
method names that are used in vtkUniformGrid which are discussed in Section~\ref{section:vtkImageData}.
......@@ -588,14 +570,13 @@ a flat array of memory. vtkIdList is commonly used for storing ids of points or
\item void Squeeze() -- reclaim any capacity not used by the list.
\end{itemize}
The following is some code demonstrating the use of vtkIdList:
\begin{cpplst}
\begin{minted}{c++}
vtkIdList* idList = vtkIdList::New();
idList->SetNumberOfIds(1);
idList->SetId(0, 5); // first Id is 5
idList->InsertId(1, 3); // second Id is 3
\end{minted}
\end{cpplst}
\subsubsection{vtkPolyData}
vtkPolyData supports all 0D, 1D and 2D VTK cell types. It derives from vtkPointSet for storing
point information but has its own internal data structures for storing cell information. For
......@@ -623,8 +604,7 @@ following method can be used to add cells to the grid, where now pts stores the
\end{itemize}
An example of creating a vtkPolyData with all quadrilaterall cells is:
\begin{cpplst}
\begin{minted}{c++}
vtkPolyData* grid = vtkPolyData::New();
vtkPoints* points = vtkPoints::New();
points->SetNumberOfPoints(11*21);
......@@ -645,8 +625,8 @@ for(int i=0;i<10;i++)
grid->InsertNextCell(9, 4, ids);
}
}
\end{minted}
\end{cpplst}
\subsubsection{vtkUnstructuredGrid}
vtkUnstructuredGrid supports all VTK cell types. It also derives from vtkPointSet for
storing point information. It uses a single vtkCellArray to store all of the cells. As
......@@ -723,8 +703,7 @@ vtkUnsignedCharArray and the cellLocations argument corresponds to the vtkIdType
An example is shown below for creating the cell topology data structures for an
unstructured grid. Note that it assumes the points have already been added to the grid.
\begin{cpplst}
\begin{minted}{c++}
// create the cell data structures
vtkCellArray* cellArray = vtkCellArray::New();
vtkIdTypeArray* offsets = vtkIdTypeArray::New();
......@@ -756,8 +735,7 @@ grid->SetCells(types, offsets, cellArray);
types->Delete();
offsets->Delete();
cellArray->Delete();
\end{cpplst}
\end{minted}
\subsubsection{Field Data}
Once the grids are created, the next step is to associate attributes with the points and/or cells of the grid.
......@@ -794,21 +772,20 @@ corresponding type. There is no explicit check when inserting arrays into either
many filters will give warnings and/or fail if this condition isn't met.
The following snippet of code demonstrates how arrays are added to point data and cell data.
\begin{cpplst}
\begin{minted}{c++}
vtkDoubleArray* pressure = vtkDoubleArray::New();
pressure->SetNumberOfTuples(grid->GetNumberOfPoints());
pressure->SetName(``pressure'');
<set values for pressure>
pressure->SetName("pressure");
<set values of pressure array>
grid->GetPointData()->AddArray(pressure);
pressure->Delete();
vtkFloatArray* temperature = vtkFloatArray::New();
temperature->SetName(``temperature'');
temperature->SetName("temperature");
temperature->SetNumberOfTuples(grid->GetNumberOfCells());
grid->GetCellData()->AddArray(temperature);
temperature->Delete();
\end{minted}
\end{cpplst}
\subsection{Multi-Block Datasets}
So far we've covered all of the main datasets and how to define attributes over them
(i.e. the point and cell data). For many situations though we will want to use multiple
......@@ -878,8 +855,7 @@ Note that vtkMultiPieceDataSet is intended to be included in other composite dat
vtkMultiBlockDataSet or vtkOverlappingAMR. There is no writer in ParaView that can handle a
vtkMultiPieceDataSet as the main input so adaptors should nest any multi-piece datasets in
a separate composite dataset. An example of creating a multi-piece dataset where we only partition the grid in the x-direction is shown below:
\begin{cpplst}
\begin{minted}{c++}
vtkImageData* imageData = vtkImageData::New();
imageData->SetSpacing(1, 1, 1);
imageData->SetExtent(0, 50, 0, 100, 0, 100);
......@@ -896,8 +872,7 @@ vtkMultiBlockDataSet* multiBlock = vtkMultiBlockDataSet::New();
multiBlock->SetNumberOfBlocks(1);
multiBlock->SetBlock(0, multiPiece);
multiPiece->Delete();
\end{cpplst}
\end{minted}
\subsubsection{vtkUniformGridAMR}
The vtkUniformGridAMR class is used to deal with AMR grids and to automate the process of
......@@ -941,7 +916,7 @@ each direction. The main function of interest for vtkAMRBox is the constructor w
\begin{itemize}
\item vtkAMRBox (const double *origin, const int *dimensions, const double *spacing, const double *globalOrigin, int gridDescription=VTK\_XYZ\_GRID) -- Here, origin is the minimum bounds of the vtkUniformGrid that the box represents, dimensions is the number of points in each of the grid's logical directions, spacing is the distance between points in each logical direction, globalOrigin is the minimum bounds of the entire composite dataset and gridDescription specifies the logical coordinates that the grid discretizes.
Once the vtkAMRBox is created for a vtkUniformGrid, the following methods should be used:
\item void SetAMRBox (unsigned int level, unsigned int id, const vtkAMRBox &box) -- level is the hierarchical level that box belongs to and id is the index at that level for the box. Note that similar to the SetDataSet() method, valid values of id are between 0 and up to but not including the global number of vtkUniformGrids at that level.
\item void SetAMRBox (unsigned int level, unsigned int id, const vtkAMRBox \&box) -- level is the hierarchical level that box belongs to and id is the index at that level for the box. Note that similar to the SetDataSet() method, valid values of id are between 0 and up to but not including the global number of vtkUniformGrids at that level.
\item void SetAMRBlockSourceIndex (unsigned int level, unsigned int id, int sourceId) -- This method is very similar to the SetDataSet() method but instead of specifying the dataset for a given level and index at that level, it specifies the sourceId in the global composite dataset hierarchy. This is the overall composite index of the dataset and can be set as the total number of datasets that exist at coarser levels plus the number of datasets
that have a lower index but are at the same level as the given dataset.
\end{itemize}
......@@ -949,8 +924,7 @@ After this has been done for each dataset, the void GenerateParentChildInformati
called. This method generates the proper relations between the blocks and the blanking inside of each block.
After this has been done, the uniform grids should be added with SetDataSet(). An example of creating a
vtkOverlappingAMR composite dataset is included below to help elucidate how all of this comes together.
\begin{cpplst}
\begin{minted}{c++}
int numberOfLevels = 3;
int blocksPerLevel[3] = {1, 1, 1};
vtkOverlappingAMR* amrGrid = vtkOverlappingAMR::New();
......@@ -962,14 +936,11 @@ double level1Spacing[] = {2, 2, 2};
double level2Spacing[] = {1, 1, 1};
amrGrid->SetOrigin(origin);
int level0Dims[] = {25, 25, 25};
vtkAMRBox level0Box(origin, level0Dims, level0Spacing, origin,
VTK_XYZ_GRID);
vtkAMRBox level0Box(origin, level0Dims, level0Spacing, origin, VTK_XYZ_GRID);
int level1Dims[] = {20, 20, 20};
vtkAMRBox level1Box(origin, level1Dims, level1Spacing, origin,
VTK_XYZ_GRID);
vtkAMRBox level1Box(origin, level1Dims, level1Spacing, origin, VTK_XYZ_GRID);
int level2Dims[] = {10, 10, 10};
vtkAMRBox level2Box(origin, level2Dims, level2Spacing, origin,
VTK_XYZ_GRID);
vtkAMRBox level2Box(origin, level2Dims, level2Spacing, origin, VTK_XYZ_GRID);
amrGrid->SetSpacing(0, level0Spacing);
amrGrid->SetAMRBox(0, 0, level0Box);
amrGrid->SetSpacing(1, level1Spacing);
......@@ -984,7 +955,7 @@ level0Grid->SetOrigin(0, 0, 0);
level0Grid->SetExtent(0, 25, 0, 25, 0, 25);
amrGrid->SetDataSet(0, 0, level0Grid);
level0Grid->Delete();
// the mid-level grid
// the middle level grid
vtkUniformGrid* level1Grid = vtkUniformGrid::New();
level1Grid->SetSpacing(level1Spacing);
level1Grid->SetExtent(0, 20, 0, 20, 0, 20);
......@@ -996,8 +967,7 @@ level2Grid->SetSpacing(level2Spacing);
level2Grid->SetExtent(0, 10, 0, 10, 0, 10);
amrGrid->SetDataSet(2, 0, level2Grid);
level2Grid->Delete();
\end{cpplst}
\end{minted}
\subsubsection{vtkNonOverlappingAMR}
The vtkNonOverlappingAMR grid is for the case of groups of vtkUniformGrids that do not overlap but can
......@@ -1006,8 +976,7 @@ assign all vtkUniformGrids to be at the coarsest level but this would remove any
may be useful by storing the grids at different levels. The methods of interested for constructing
non-overlapping composite datasets are all in its vtkUniformGridAMR superclass. An example is included
below which demonstrates the construction of a vtkNonOverlappingAMR grid.
\begin{cpplst}
\begin{minted}{c++}
int numberOfLevels = 3;
int blocksPerLevel[3] = {1, 2, 1};