Difference between revisions of "ParaView/Users Guide/Python Programmable Filter"

From KitwarePublic
Jump to: navigation, search
(Intermediate Use)
(13 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
== Introduction ==
 
== Introduction ==
  
[[Image:ParaView_UG_ProgrammableFilter.png|400px|left]]
+
[[Image:ParaView_UG_ProgrammableFilter.png|thumb|400px|left|'''Figure 6.1''']]
  
The Programmable Filter is a ParaView filter that processes one or more input datasets based on a Python script provided by the user. The parameters of the filter include the output data type, the script and a toggle that controls whether the input arrays are copied to the output. In this document, we introduce the use of the Programmable Filter and give a summary of the API available to the user.  
+
The Programmable Filter is a ParaView filter that processes one or more input datasets based on a Python script provided by the user. The parameters of the filter include the output data type, the script and a toggle that controls whether the input arrays are copied to the output. This chapter introduces the use of the Programmable Filter and gives a summary of the API available to the user.  
  
 
Note that the Programmable Filter depends on Python. All ParaView binaries distributed by Kitware are built with Python enabled. If you have built ParaView yourself, you have to make sure that PARAVIEW_ENABLE_PYTHON is turned on when configuring the ParaView build.
 
Note that the Programmable Filter depends on Python. All ParaView binaries distributed by Kitware are built with Python enabled. If you have built ParaView yourself, you have to make sure that PARAVIEW_ENABLE_PYTHON is turned on when configuring the ParaView build.
  
Since the entire VTK API as well as any module that can be imported through Python is available through this filter, we can only skim the surface of what can be accomplished with this filter here. If you are not familiar with Python, we recommend first taking a look at one of the introductory guides such as the [http://docs.python.org/tutorial/ official Python Tutorial]. Also, if you are going to do any programming beyond the very basic stuff, we recommend reading up on the VTK API. [http://www.vtk.org The VTK website] has links to VTK books and online documentation. For reference, you may need to look at the [http://www.vtk.org/doc/release/5.6/html/ VTK class documentation]. There is also more information about the Programmable Filter and some good recipes on the ParaView Wiki ([[Python_Programmable_Filter]]).
+
Since the entire VTK API as well as any module that can be imported through Python is available through this filter, we can only skim the surface of what can be accomplished with this filter here. If you are not familiar with Python, we recommend first taking a look at one of the introductory guides such as the [http://docs.python.org/tutorial/ official Python Tutorial]. If you are going to do any programming beyond the very basics, we recommend reading up on the VTK API. [http://www.vtk.org The VTK website] has links to VTK books and online documentation. For reference, you may need to look at the [http://www.vtk.org/doc/release/5.6/html/ VTK class documentation]. There is also more information about the Programmable Filter and some good recipes on the ParaView Wiki ([[Python_Programmable_Filter]]).
  
 
== Basic Use ==
 
== Basic Use ==
Line 15: Line 15:
 
# You have NumPy installed.
 
# You have NumPy installed.
  
The most basic reason to use the Programmable Filter is to add a new array by possible deriving it from arrays in the input. This can be achieved by using the Python Calculator. One reason to use the Programmable Filter instead may be that the calculation is more involved and trying to do it in one expression may be difficult. Another reason may be that you need access to a program flow construct such as if or for. In any case. the Programmable Filter can be used to do everything the Calculator does and more.
+
The most basic reason to use the Programmable Filter is to add a new array by deriving it from arrays in the input, which can also be achieved by using the Python Calculator. One reason to use the Programmable Filter instead may be that the calculation is more involved and trying to do it in one expression may be difficult. Another reason may be that you need access to a program flow construct such as if or for. The Programmable Filter can be used to do everything the Calculator does and more.
  
'''Note:''' Since what we describe here builds on some of the concepts introduced in the [[ParaView/Users_Guide/Python_Calculator | Python Calculator section]], please read it first if you are not familiar with the Calculator.
+
'''Note:''' Since what is described here builds on some of the concepts introduced in the [[ParaView/Users_Guide/Python_Calculator | Python Calculator section]], please read it first if you are not familiar with the Calculator.
  
If you leave the "Output Dataset Type" parameter in the default setting of "Same as Input", the Programmable Filter will copy the topology and geometry of the input to the output before calling your Python script. Therefore, if you Apply the filter without filling the script, you should see a copy of the input without any of its arrays in the output. If you also check the "Copy Arrays" option, the output will have all of the input arrays. This behavior allows you to focus on creating new arrays without worrying about the mesh.
+
If you leave the "Output Dataset Type" parameter in the default setting of "Same as Input," the Programmable Filter will copy the topology and geometry of the input to the output before calling your Python script. Therefore, if you Apply the filter without filling the script, you should see a copy of the input without any of its arrays in the output. If you also check the Copy Arrays option, the output will have all of the input arrays. This behavior allows you to focus on creating new arrays without worrying about the mesh.
  
Let's try an example. Create a Sphere source and then apply the Programmable Filter. Use the following script.
+
Create a Sphere source and then apply the Programmable Filter and use the following script.
  
 
<source lang="python">
 
<source lang="python">
Line 28: Line 28:
 
</source>
 
</source>
  
This should create a sphere with on array called "Normals_x". There a few things to note here:
+
This should create a sphere with an array called "Normals_x". There a few things to note here:
  
 
* You cannot refer to arrays directly by name as in the Python Calculator. You need to access arrays using the .PointData and .CellData qualifiers.
 
* You cannot refer to arrays directly by name as in the Python Calculator. You need to access arrays using the .PointData and .CellData qualifiers.
Line 43: Line 43:
 
== Intermediate Use ==
 
== Intermediate Use ==
  
The examples above demonstrate how the Programmable Filter can be used as an advanced Python Calculator. However, the full power of the Programmable Filter can only be harnessed by using the VTK API. Let's start with a simple example. Create a Sphere source and apply the Programmable Filter with the following script.
+
=== Mixing VTK and NumPy APIs ===
 +
 
 +
The previous examples demonstrate how the Programmable Filter can be used as an advanced Python Calculator. However, the full power of the Programmable Filter can only be harnessed by using the VTK API. The following is a simple example. Create a Sphere source and apply the Programmable Filter with the following script.
  
 
<source lang="python">
 
<source lang="python">
Line 57: Line 59:
 
</source>
 
</source>
  
This requires some explanation. We start with creating a new instance of vtkPoints.
+
Start with creating a new instance of vtkPoints:
  
 
<source lang="python">
 
<source lang="python">
Line 63: Line 65:
 
</source>
 
</source>
  
vtkPoints is a data structure that VTK uses to store the coordinates of points. Next, we loop over all points of the input and insert a new point in the output with coordinates (x, y, 1+z*0.3)
+
vtkPoints is a data structure that VTK uses to store the coordinates of points. Next, loop over all points of the input and insert a new point in the output with coordinates (x, y, 1+z*0.3)
  
 
<source lang="python">
 
<source lang="python">
Line 71: Line 73:
 
</source>
 
</source>
  
Finally, we replace the output points with the new points we created using the following.
+
Finally, replace the output points with the new points we created using the following:
  
 
<source lang="python">
 
<source lang="python">
Line 79: Line 81:
 
'''Note:''' Python is an interpreted language and Python scripts do not execute as efficiently as compiled C++ code. Therefore, using a for loop that iterates over all points or cells may be a significant bottleneck when processing large datasets.
 
'''Note:''' Python is an interpreted language and Python scripts do not execute as efficiently as compiled C++ code. Therefore, using a for loop that iterates over all points or cells may be a significant bottleneck when processing large datasets.
  
The NumPy and VTK APIs can be mixed to achieve good performance. Even though this may seem a bit complicated at first, it can be used with great effect. For example, the example above can be rewritten as follows.
+
The NumPy and VTK APIs can be mixed to achieve good performance. Even though this may seem a bit complicated at first, it can be used with great effect. For instance, the example above can be rewritten as follows.
  
 
<source lang="python">
 
<source lang="python">
Line 90: Line 92:
 
zs = 1 + input.Points[:,2]*0.3
 
zs = 1 + input.Points[:,2]*0.3
 
coords = hstack([input.Points[:,0:2],zs])
 
coords = hstack([input.Points[:,0:2],zs])
output.PointData.append(coords, 'coords')
+
 
 
newPoints.SetData(numpyTovtkDataArray(coords))
 
newPoints.SetData(numpyTovtkDataArray(coords))
 +
 
output.SetPoints(newPoints)
 
output.SetPoints(newPoints)
 
</source>
 
</source>
  
== Advanced (but read it anyway) ==
+
Even though this produces exactly the same result, it is much more efficient because the for loop was moved it from Python to C. Under the hood, NumPy uses C and Fortran for tight loops.
 +
 
 +
If you read the Python Calculator documentation, this example is straightforward except the use of numpyTovtkDataArray(). First, note that you are mixing two APIs here: the VTK API and NumPy. VTK and NumPy uses different types of objects to represents arrays. The basic examples previously used carefully hide this from you. However, once you start manipulating VTK objects using NumPy, you have to start converting objects between two APIs. Note that for the most part this conversion happens without "deep copying" arrays, for example copying the raw contents from one memory location to another. Rather, pointers are passed between VTK and NumPy whenever possible.
 +
 
 +
The dataset_adapter provides two methods to do the conversions described above:
 +
 
 +
* vtkDataArrayToVTKArray: This function creates a NumPy compatible array from a vtkDataArray. Note that VTKArray is actually a subclass of numpy.matrix and can be used anywhere matrix can be used. This function always copies the pointer and not the contents. '''Important:''' You should not directly change the values of the resulting array if the argument is an array from the input.
 +
* numpyTovtkDataArray: Converts a NumPy array (or a VTKArray) to a vtkDataArray. This function copies the pointer if the argument is a contiguous array. There are various ways of creating discontinuous arrays with NumPy including using hstack and striding. See NumPy documentation for details.
 +
 
 +
=== Multiple Inputs ===
 +
 
 +
Like the Python Calculator, the Programmable Filter can accept multiple inputs. First, select two or more pipeline objects in the pipeline browser and then apply the Programmable Filter. Then each input can be accessed using the inputs[] variable. Note that if the Output Dataset Type is set to Same as Input, the filter will copy the mesh from the first input to the output. If Copy Arrays is on, it will also copy arrays from the first input. As an example, the following script compares the Pressure attribute from two inputs using the difference operator.
 +
 
 +
<source lang="python">
 +
output.append(inputs[1].PointData['Pressure'] - inputs[0].PointData['Pressure'], "difference")
 +
</source>
 +
 
 +
=== Dealing with Composite Datasets ===
 +
 
 +
Thus far, none of the examples used apply to multi-block or AMR datasets. When talking about the Python Calculator, you did not have to differentiate between simple and composite datasets. This is because the calculator loops over all of the leaf blocks of composite datasets and applies the expression to each one. Therefore, inputs in an expression are guaranteed to be simple datasets. On the other hand, the Programmable Filter does not perform this iteration and passes the input, composite or simple, as it is to the script. Even though this makes basic scripting harder for composite datasets, it provides enormous flexibility.
 +
 
 +
To work with composite datasets you need to know how to iterate over them to access the leaf nodes.
 +
 
 +
<source lang="python">
 +
for block in inputs[0]:
 +
print block
 +
</source>
 +
 
 +
Here you iterate over all of the non-NULL leaf nodes (i.e. simple datasets) of the input and print them to the Output Messages console. Note that this will work only if the input is multi-block or AMR.
 +
 
 +
When Output Dataset Type is set to "Same as Input," the Programmable Filter will copy composite dataset to the output - it will copy only the mesh unless Copy Arrays is on. Therefore, you can also iterate over the output. A simple trick is to turn on Copy Arrays and then use the arrays from the output when generating new ones. Below is an example. You should use the can.ex2 file from the ParaView testing dataset collection.
 +
 
 +
<source lang="python">
 +
def process_block(block):
 +
displ = block.PointData['DISPL']
 +
block.PointData.append(displ[:,0], "displ_x")
 +
 
 +
for block in output:
 +
process_block(block)
 +
</source>
 +
 
 +
Alternatively, you can use the MultiCompositeDataIterator to iterate over the input and output block simultaneously. The following is equivalent to the previous example:
 +
 
 +
<source lang="python">
 +
def process_block(input_block, output_block):
 +
displ = input_block.PointData['DISPL']
 +
output_block.PointData.append(displ[:,0], "displ_x")
 +
 
 +
from paraview.vtk.dataset_adapter import MultiCompositeDataIterator
 +
iter = MultiCompositeDataIterator([inputs[0], output])
 +
 
 +
for input_block, output_block in iter:
 +
process_block(input_block, output_block)
 +
</source>
 +
 
 +
== Advanced ==
 +
 
 +
=== Changing Output Type ===
 +
 
 +
Thus far, all of the examples discussed depended on the output type being the same as input and that the Programmable Filter copied the input mesh to the output. If you set the output type to something other than Same as Input, the Programmable Filter will create an empty output of the type we specified but will not copy any information. Even though it may be more work, this provides a lot of flexibility. Since this is approaching the realm of VTK filter authoring, a very simple example is used. If you are already familiar with VTK API, you will realize that this is a great way of prototyping VTK filters. If you are not, reading up on VTK is recommended.
 +
 
 +
Create a Wavelet source, apply a Programmable Filter, set the output type to vtkTable and use the following script:
 +
 
 +
<source lang="python">
 +
rtdata = inputs[0].PointData['RTData']
 +
 
 +
output.RowData.append(min(rtdata), 'min')
 +
output.RowData.append(max(rtdata), 'max')
 +
</source>
 +
 
 +
Here, you added two columns to the output table. The first one has one value - minimum of RTData - and the second one has the maximum of RTData. When you apply this filter, the output should automatically be shown in a Spreadsheet view. You could also use this sort of script to chart part of the input data. For example, the output of the following script can be display as a line chart.
 +
 
 +
<source lang="python">
 +
rtdata = inputs[0].PointData['RTData']
 +
output.RowData.append(rtdata, 'rtdata')
 +
</source>
 +
 
 +
Changing the output type is also often necessary when using VTK filters within the script, which is demonstrated in the following section.
 +
 
 +
=== Dealing with Structured Data Output ===
 +
 
 +
A curvilinear gris, for instance a bluntfin.vts from the ParaView testing data, is used as a good example. If you would like to volume render a subset of this grid, since as of 3.10, ParaView does not support volume rendering of curvilinear grids, you have two choices:
 +
 
 +
* Resample to an image data
 +
* Convert to unstructured grid
 +
This example demonstrates how to resample to image data using the Programmable Filter. This can be accomplished using the Resample with Dataset filter, but it is a good example nevertheless. Start with loading bluntfin.vts, then apply the Programmable Filter. Make sure to set the output type to vtkImageData. Here is the script:
 +
 
 +
<source lang="python">
 +
pinput = vtk.vtkImageData()
 +
pinput.SetExtent(0, 10, 0, 10, 0, 10)
 +
pinput.SetOrigin(0, 1, 0)
 +
pinput.SetSpacing(0.5, 0.5, 0.5)
 +
 
 +
probe = vtk.vtkProbeFilter()
 +
probe.SetInput(pinput)
 +
 
 +
input_copy = inputs[0].NewInstance()
 +
input_copy.UnRegister(None)
 +
input_copy.ShallowCopy(inputs[0].VTKObject)
 +
 
 +
probe.SetSource(input_copy)
 +
probe.Update()
 +
 
 +
output.ShallowCopy(probe.GetOutput())
 +
</source>
 +
 
 +
'''Note:''' See the next section for details about using a VTK filter within the Programmable Filter.
 +
 
 +
If you already applied, you may notice that the output looks much bigger than it should be because an important piece is missing. You need to use the following as the RequestInformation script:
 +
 
 +
<source lang="python">
 +
from paraview.util import SetOutputWholeExtent
 +
 
 +
SetOutputWholeExtent(self, [0, 10, 0, 10, 0, 10])
 +
</source>
 +
 
 +
VTK expects that all data sources and filters that produce structured data (rectilinear or curvilinear grids) to provide meta data about the logical extents of the output dataset before full execution. Thus the RequestInformation is called by the Programmable Filter before execution and is where you should provide this meta data. This is not required if the filter is simply copying the mesh as the meta data would have been provided by another pipeline object upstream. However, if you are using the Programmable Filter to produce a structured data with a different mesh than the input, you need to provide this information.
 +
 
 +
The RequestUpdateExtent script can be used to augment the request that propagates upstream before execution. This is used to ask for a specific data extent, for example. This is an advanced concept and is not discussed further here.
 +
 
 +
=== Using VTK Filters with Programmable Filter ===
 +
 
 +
The previous example demonstrated how you can use a VTK filter (vtkProbeFilter in this case) from with the Programmable Filter. We will explain that example in more detail here.
 +
 
 +
<source lang="python">
 +
pinput = vtk.vtkImageData()
 +
pinput.SetExtent(0, 10, 0, 10, 0, 10)
 +
pinput.SetOrigin(0, 1, 0)
 +
pinput.SetSpacing(0.5, 0.5, 0.5)
 +
 
 +
probe = vtk.vtkProbeFilter()
 +
probe.SetInput(pinput)
 +
 
 +
input_copy = inputs[0].NewInstance()
 +
input_copy.UnRegister(None)
 +
input_copy.ShallowCopy(inputs[0].VTKObject)
 +
 
 +
probe.SetSource(input_copy)
 +
probe.Update()
 +
 
 +
output.ShallowCopy(probe.GetOutput())
 +
</source>
 +
 
 +
There are two important tricks to use a VTK filter from another VTK filter. First, do not directly set the input to the outer filter as the input of the inner filter. (It is difficult to explain why without getting into VTK pipeline mechanics). Instead, make a shallow copy as follows:
 +
 
 +
<source lang="python">
 +
input_copy = inputs[0].NewInstance()
 +
input_copy.UnRegister(None)
 +
input_copy.ShallowCopy(inputs[0].VTKObject)
 +
</source>
 +
 
 +
The UnRegister() call is essential to avoid memory leaks.
 +
 
 +
The second trick is to use ShallowCopy() to copy the output of the internal filter to the output of the outer filter as follows:
 +
 
 +
<source lang="python">
 +
output.ShallowCopy(probe.GetOutput())
 +
</source>
 +
 
 +
This should be enough to get you started. There are a large number of VTK filters so it is not possible to describe them here. Refer to the VTK documentation for more information.

Revision as of 13:54, 9 June 2011

Introduction

Figure 6.1

The Programmable Filter is a ParaView filter that processes one or more input datasets based on a Python script provided by the user. The parameters of the filter include the output data type, the script and a toggle that controls whether the input arrays are copied to the output. This chapter introduces the use of the Programmable Filter and gives a summary of the API available to the user.

Note that the Programmable Filter depends on Python. All ParaView binaries distributed by Kitware are built with Python enabled. If you have built ParaView yourself, you have to make sure that PARAVIEW_ENABLE_PYTHON is turned on when configuring the ParaView build.

Since the entire VTK API as well as any module that can be imported through Python is available through this filter, we can only skim the surface of what can be accomplished with this filter here. If you are not familiar with Python, we recommend first taking a look at one of the introductory guides such as the official Python Tutorial. If you are going to do any programming beyond the very basics, we recommend reading up on the VTK API. The VTK website has links to VTK books and online documentation. For reference, you may need to look at the VTK class documentation. There is also more information about the Programmable Filter and some good recipes on the ParaView Wiki (Python_Programmable_Filter).

Basic Use

Requirements:

  1. You are applying Programmable Filter to a "simple" dataset and not a composite dataset such as multi-block or AMR.
  2. You have NumPy installed.

The most basic reason to use the Programmable Filter is to add a new array by deriving it from arrays in the input, which can also be achieved by using the Python Calculator. One reason to use the Programmable Filter instead may be that the calculation is more involved and trying to do it in one expression may be difficult. Another reason may be that you need access to a program flow construct such as if or for. The Programmable Filter can be used to do everything the Calculator does and more.

Note: Since what is described here builds on some of the concepts introduced in the Python Calculator section, please read it first if you are not familiar with the Calculator.

If you leave the "Output Dataset Type" parameter in the default setting of "Same as Input," the Programmable Filter will copy the topology and geometry of the input to the output before calling your Python script. Therefore, if you Apply the filter without filling the script, you should see a copy of the input without any of its arrays in the output. If you also check the Copy Arrays option, the output will have all of the input arrays. This behavior allows you to focus on creating new arrays without worrying about the mesh.

Create a Sphere source and then apply the Programmable Filter and use the following script.

normals = inputs[0].PointData['Normals']
output.PointData.append(normals[:,0], "Normals_x")

This should create a sphere with an array called "Normals_x". There a few things to note here:

  • You cannot refer to arrays directly by name as in the Python Calculator. You need to access arrays using the .PointData and .CellData qualifiers.
  • Unlike the Python Calculator, you have to explicitly add an array to the output using the append function. Note that this function takes the name of the array as the second argument.

You can use any of the functions available in the Calculator in the Programmable Filter. For example, the following code creates two new arrays and adds them to the output.

normals = inputs[0].PointData['Normals']
output.PointData.append(sin(normals[:,0]), "sin of Normals_x")
output.PointData.append(normals[:,1] + 1, "Normals_y + 1")

Intermediate Use

Mixing VTK and NumPy APIs

The previous examples demonstrate how the Programmable Filter can be used as an advanced Python Calculator. However, the full power of the Programmable Filter can only be harnessed by using the VTK API. The following is a simple example. Create a Sphere source and apply the Programmable Filter with the following script.

input = inputs[0]
 
newPoints = vtk.vtkPoints()
numPoints = input.GetNumberOfPoints()
for i in range(numPoints):
    x, y, z = input.GetPoint(i)
    newPoints.InsertPoint(i, x, y, 1 + z*0.3)
 
output.SetPoints(newPoints)

Start with creating a new instance of vtkPoints:

newPoints = vtk.vtkPoints()

vtkPoints is a data structure that VTK uses to store the coordinates of points. Next, loop over all points of the input and insert a new point in the output with coordinates (x, y, 1+z*0.3)

for i in range(numPoints):
    x, y, z = input.GetPoint(i)
    newPoints.InsertPoint(i, x, y, 1 + z*0.3)

Finally, replace the output points with the new points we created using the following:

output.SetPoints(newPoints)

Note: Python is an interpreted language and Python scripts do not execute as efficiently as compiled C++ code. Therefore, using a for loop that iterates over all points or cells may be a significant bottleneck when processing large datasets.

The NumPy and VTK APIs can be mixed to achieve good performance. Even though this may seem a bit complicated at first, it can be used with great effect. For instance, the example above can be rewritten as follows.

from paraview.vtk.dataset_adapter import numpyTovtkDataArray
 
input = inputs[0]
 
newPoints = vtk.vtkPoints()
 
zs = 1 + input.Points[:,2]*0.3
coords = hstack([input.Points[:,0:2],zs])
 
newPoints.SetData(numpyTovtkDataArray(coords))
 
output.SetPoints(newPoints)

Even though this produces exactly the same result, it is much more efficient because the for loop was moved it from Python to C. Under the hood, NumPy uses C and Fortran for tight loops.

If you read the Python Calculator documentation, this example is straightforward except the use of numpyTovtkDataArray(). First, note that you are mixing two APIs here: the VTK API and NumPy. VTK and NumPy uses different types of objects to represents arrays. The basic examples previously used carefully hide this from you. However, once you start manipulating VTK objects using NumPy, you have to start converting objects between two APIs. Note that for the most part this conversion happens without "deep copying" arrays, for example copying the raw contents from one memory location to another. Rather, pointers are passed between VTK and NumPy whenever possible.

The dataset_adapter provides two methods to do the conversions described above:

  • vtkDataArrayToVTKArray: This function creates a NumPy compatible array from a vtkDataArray. Note that VTKArray is actually a subclass of numpy.matrix and can be used anywhere matrix can be used. This function always copies the pointer and not the contents. Important: You should not directly change the values of the resulting array if the argument is an array from the input.
  • numpyTovtkDataArray: Converts a NumPy array (or a VTKArray) to a vtkDataArray. This function copies the pointer if the argument is a contiguous array. There are various ways of creating discontinuous arrays with NumPy including using hstack and striding. See NumPy documentation for details.

Multiple Inputs

Like the Python Calculator, the Programmable Filter can accept multiple inputs. First, select two or more pipeline objects in the pipeline browser and then apply the Programmable Filter. Then each input can be accessed using the inputs[] variable. Note that if the Output Dataset Type is set to Same as Input, the filter will copy the mesh from the first input to the output. If Copy Arrays is on, it will also copy arrays from the first input. As an example, the following script compares the Pressure attribute from two inputs using the difference operator.

output.append(inputs[1].PointData['Pressure'] - inputs[0].PointData['Pressure'], "difference")

Dealing with Composite Datasets

Thus far, none of the examples used apply to multi-block or AMR datasets. When talking about the Python Calculator, you did not have to differentiate between simple and composite datasets. This is because the calculator loops over all of the leaf blocks of composite datasets and applies the expression to each one. Therefore, inputs in an expression are guaranteed to be simple datasets. On the other hand, the Programmable Filter does not perform this iteration and passes the input, composite or simple, as it is to the script. Even though this makes basic scripting harder for composite datasets, it provides enormous flexibility.

To work with composite datasets you need to know how to iterate over them to access the leaf nodes.

for block in inputs[0]:
	print block

Here you iterate over all of the non-NULL leaf nodes (i.e. simple datasets) of the input and print them to the Output Messages console. Note that this will work only if the input is multi-block or AMR.

When Output Dataset Type is set to "Same as Input," the Programmable Filter will copy composite dataset to the output - it will copy only the mesh unless Copy Arrays is on. Therefore, you can also iterate over the output. A simple trick is to turn on Copy Arrays and then use the arrays from the output when generating new ones. Below is an example. You should use the can.ex2 file from the ParaView testing dataset collection.

def process_block(block):
	displ = block.PointData['DISPL']
	block.PointData.append(displ[:,0], "displ_x")
 
for block in output:
	process_block(block)

Alternatively, you can use the MultiCompositeDataIterator to iterate over the input and output block simultaneously. The following is equivalent to the previous example:

def process_block(input_block, output_block):
	displ = input_block.PointData['DISPL']
	output_block.PointData.append(displ[:,0], "displ_x")
 
from paraview.vtk.dataset_adapter import MultiCompositeDataIterator
iter = MultiCompositeDataIterator([inputs[0], output])
 
for input_block, output_block in iter:
	process_block(input_block, output_block)

Advanced

Changing Output Type

Thus far, all of the examples discussed depended on the output type being the same as input and that the Programmable Filter copied the input mesh to the output. If you set the output type to something other than Same as Input, the Programmable Filter will create an empty output of the type we specified but will not copy any information. Even though it may be more work, this provides a lot of flexibility. Since this is approaching the realm of VTK filter authoring, a very simple example is used. If you are already familiar with VTK API, you will realize that this is a great way of prototyping VTK filters. If you are not, reading up on VTK is recommended.

Create a Wavelet source, apply a Programmable Filter, set the output type to vtkTable and use the following script:

rtdata = inputs[0].PointData['RTData']
 
output.RowData.append(min(rtdata), 'min')
output.RowData.append(max(rtdata), 'max')

Here, you added two columns to the output table. The first one has one value - minimum of RTData - and the second one has the maximum of RTData. When you apply this filter, the output should automatically be shown in a Spreadsheet view. You could also use this sort of script to chart part of the input data. For example, the output of the following script can be display as a line chart.

rtdata = inputs[0].PointData['RTData']
output.RowData.append(rtdata, 'rtdata')

Changing the output type is also often necessary when using VTK filters within the script, which is demonstrated in the following section.

Dealing with Structured Data Output

A curvilinear gris, for instance a bluntfin.vts from the ParaView testing data, is used as a good example. If you would like to volume render a subset of this grid, since as of 3.10, ParaView does not support volume rendering of curvilinear grids, you have two choices:

  • Resample to an image data
  • Convert to unstructured grid

This example demonstrates how to resample to image data using the Programmable Filter. This can be accomplished using the Resample with Dataset filter, but it is a good example nevertheless. Start with loading bluntfin.vts, then apply the Programmable Filter. Make sure to set the output type to vtkImageData. Here is the script:

pinput = vtk.vtkImageData()
pinput.SetExtent(0, 10, 0, 10, 0, 10)
pinput.SetOrigin(0, 1, 0)
pinput.SetSpacing(0.5, 0.5, 0.5)
 
probe = vtk.vtkProbeFilter()
probe.SetInput(pinput)
 
input_copy = inputs[0].NewInstance()
input_copy.UnRegister(None)
input_copy.ShallowCopy(inputs[0].VTKObject)
 
probe.SetSource(input_copy)
probe.Update()
 
output.ShallowCopy(probe.GetOutput())

Note: See the next section for details about using a VTK filter within the Programmable Filter.

If you already applied, you may notice that the output looks much bigger than it should be because an important piece is missing. You need to use the following as the RequestInformation script:

from paraview.util import SetOutputWholeExtent
 
SetOutputWholeExtent(self, [0, 10, 0, 10, 0, 10])

VTK expects that all data sources and filters that produce structured data (rectilinear or curvilinear grids) to provide meta data about the logical extents of the output dataset before full execution. Thus the RequestInformation is called by the Programmable Filter before execution and is where you should provide this meta data. This is not required if the filter is simply copying the mesh as the meta data would have been provided by another pipeline object upstream. However, if you are using the Programmable Filter to produce a structured data with a different mesh than the input, you need to provide this information.

The RequestUpdateExtent script can be used to augment the request that propagates upstream before execution. This is used to ask for a specific data extent, for example. This is an advanced concept and is not discussed further here.

Using VTK Filters with Programmable Filter

The previous example demonstrated how you can use a VTK filter (vtkProbeFilter in this case) from with the Programmable Filter. We will explain that example in more detail here.

pinput = vtk.vtkImageData()
pinput.SetExtent(0, 10, 0, 10, 0, 10)
pinput.SetOrigin(0, 1, 0)
pinput.SetSpacing(0.5, 0.5, 0.5)
 
probe = vtk.vtkProbeFilter()
probe.SetInput(pinput)
 
input_copy = inputs[0].NewInstance()
input_copy.UnRegister(None)
input_copy.ShallowCopy(inputs[0].VTKObject)
 
probe.SetSource(input_copy)
probe.Update()
 
output.ShallowCopy(probe.GetOutput())

There are two important tricks to use a VTK filter from another VTK filter. First, do not directly set the input to the outer filter as the input of the inner filter. (It is difficult to explain why without getting into VTK pipeline mechanics). Instead, make a shallow copy as follows:

input_copy = inputs[0].NewInstance()
input_copy.UnRegister(None)
input_copy.ShallowCopy(inputs[0].VTKObject)

The UnRegister() call is essential to avoid memory leaks.

The second trick is to use ShallowCopy() to copy the output of the internal filter to the output of the outer filter as follows:

output.ShallowCopy(probe.GetOutput())

This should be enough to get you started. There are a large number of VTK filters so it is not possible to describe them here. Refer to the VTK documentation for more information.