VisIt-tutorial-Advanced-scripting
This section consists of 2 parts. The first part describes how to use advanced features within VisIt's Python interface, specifically callback functions. The second part describes how to use Python filters within VisIt's compute engine to process data directly, giving you the ability to calculate quantities that cannot be computed within VisIt's expression language.
Contents
Callback functions
Callback functions let you attach your own Python routines to VisIt actions and changes in state. For more information see, Python callbacks.
Setting Limits on time change example
This example installs a Python callback on the TimeSliderSetState action in VisIt, which is the action that happens when you move the time slider in the Main window. The script contains a class called Limits that queries the min,max values for the active plot and uses those to expand its overall min,max values over time as you change time steps with the time slider. As the min,max values get expanded, the Limits class sets the minimum,maximum extents for the Pseudocolor plot to 10% and 90% of the total data range.
Script
# A class that sets Pseudcolor limits based on the min,max that
# have been encountered
class Limits:
def __init__(self):
self.min = 1.e100
self.max = -1.e100
def updateLimits(self):
Query("MinMax")
extents = GetQueryOutputValue()
if extents[0] < self.min:
self.min = extents[0]
if extents[1] > self.max:
self.max = extents[1]
lower = self.min + 0.1 * (self.max - self.min)
upper = self.max - 0.1 * (self.max - self.min)
pc = PseudocolorAttributes(1)
pc.minFlag = 1
pc.min = lower
pc.maxFlag = 1
pc.max = upper
SetPlotOptions(pc)
# Callback function
def onTimeSliderSetState(timeState, limits):
limits.updateLimits()
limits = Limits()
RegisterCallback("SetTimeSliderStateRPC", onTimeSliderSetState, limits)
Steps
- Run visit
- Open wave.visit or wave*.silo database files
- Add a Pseudocolor plot of pressure
- Draw the plots
- Open the Controls->Command window
- Paste the script into tab 1 and click the Execute button. This will launch VisIt's Python interface and install the Python callback function
- Drag the time slider around in the Main window and release it to change time steps. Do this often until you have visited most or all time steps.
- Open the Pseudocolor plot attributes and examine the minimum and maximum values used to clamp the plot's values. The values should be close to 0.1062 and 0.9007, respectively.
Remove Cells Example
Sometimes it is useful to remove arbitrary cells from a mesh. VisIt provides a Threshold operator that can be used to remove cells from a mesh but it takes scalars with a range of values. A list of cells does not usually represent a contiguous range of values. This example shows how to:
- Use the map expression
- Use the Threshold operator
- Use callbacks to remove cells clicked on using zone-pick
Script
class RemoveZonePickCells:
def __init__(self):
self.pickOutput = ""
self.zones = []
def getzone(self):
lines = GetPickOutput().split("\n")
for line in lines:
start = line.find("Zone: ")
if start != -1:
return int(line[start+6:])
return -1
def zone_is_new(self, zoneid):
return zoneid != -1 and zoneid not in self.zones
def addzone(self, zoneid):
self.zones.append(zoneid)
return len(self.zones)
def expressiondef(self):
pl = GetPlotList()
activePlots = []
meshName = ""
for i in range(pl.GetNumPlots()):
if pl.GetPlots(i).activeFlag:
activePlots.append(i)
if meshName != "":
continue
md = GetMetaData(pl.GetPlots(i).databaseName)
plotVar = pl.GetPlots(i).plotVar
for s in range(md.GetNumScalars()):
if md.GetScalars(s).name == plotVar:
meshName = md.GetScalars(s).meshName
break
ones=str([1]*len(self.zones))
exprdef="map(zoneid("+meshName+"),"+str(self.zones)+","+ones+",0)"
return exprdef,activePlots
def removezone(self, zoneid):
if zoneid != -1 and self.zone_is_new(zoneid):
nzones = self.addzone(zoneid)
exprdef,activePlots = self.expressiondef()
DefineScalarExpression("rmpickcells", exprdef)
if len(activePlots) == 1:
SetActivePlots(activePlots[0])
else:
SetActivePlots(activePlots)
if nzones == 1:
AddOperator("Threshold")
ThresholdAtts = ThresholdAttributes()
ThresholdAtts.outputMeshType = 0
ThresholdAtts.listedVarNames = ("rmpickcells")
ThresholdAtts.zonePortions = (1)
ThresholdAtts.lowerBounds = (0)
ThresholdAtts.upperBounds = (0.5)
ThresholdAtts.defaultVarName = "rmpickcells"
ThresholdAtts.defaultVarIsScalar = 1
SetOperatorOptions(ThresholdAtts)
else:
ClearWindow()
DrawPlots()
def onPickAttributes(pa, rmpickcells):
zoneid = rmpickcells.getzone()
rmpickcells.removezone(zoneid)
rmpickcells = RemoveZonePickCells()
RegisterCallback("PickAttributes", onPickAttributes, rmpickcells)
SuppressQueryOutputOn()
Steps
- Copy the script to a file called rmpickcells.py
- Run visit -cli -s rmpickcells.py -o example.silo
- Add a Pseudocolor plot of temp_slice by typing AddPlot("Pseudocolor", "temp_slice")
- Draw the plots by typing DrawPlots()
- Click on the zone-pick icon in the vis window
- Click on various cells in the plots and watch them disappear
Python Expression Tutorial
(Borrowed from Cyrus' Python expressions page)
Create a new expression filter via the 'Expressions' Window
- Open rect2d.silo
- Open the 'Expressions' window
- Create a new expression 'd_wave'.
- Click on the 'Python Expression Editor' Tab & you should see the following:
- Like regular VisIt expressions, python filter expressions operate on database variables and other expressions. We want to use the field 'd' as an input to our new expression. Add 'd' to the 'Arguments' text box.
- Copy and paste the example expression script below into the 'Python Expression Script' text box and click 'Apply'. This expression perturbs an input scalar (in our case 'd') by a simple wave pattern.
from math import sin, pi
class MyExpression(SimplePythonExpression):
def __init__(self):
SimplePythonExpression.__init__(self)
self.name = "MyExpression"
self.description = "Perturb input scalar by simple wave pattern."
self.output_is_point_var = False
self.output_dimension = 1
def modify_contract(self,contract):
pass
def derive_variable(self,ds_in,domain_id):
# ds_in is a vtk dataset, we want
# to create and return a new vtkDataArray
# that contains a simple sine wave pattern
ds_bounds = ds_in.GetBounds()
x_ext = ds_bounds[1] - ds_bounds[0]
y_ext = ds_bounds[3] - ds_bounds[2]
z_ext = ds_bounds[5] - ds_bounds[4]
cell_vals = ds_in.GetCellData().GetArray(self.input_var_names[0])
ncells = ds_in.GetNumberOfCells()
res = vtk.vtkFloatArray()
res.SetNumberOfComponents(1)
res.SetNumberOfTuples(ncells)
for i in xrange(ncells):
cell = ds_in.GetCell(i)
bounds = cell.GetBounds()
xv = bounds[0] + bounds[1] / 2.0
yv = bounds[2] + bounds[3] / 2.0
zv = bounds[4] + bounds[5] / 2.0
val = .05*sin(xv*3*pi/x_ext) + .05*sin(yv * 3*pi / y_ext)
if z_ext != 0:
val+= .05*sin(zv * 3*pi / z_ext)
val += cell_vals.GetTuple1(i)
res.SetTuple1(i,val)
return res
py_filter = MyExpression
- Create a Pseudocolor plot of 'd_wave', add an Elevate Operator and click draw. Here is an example the output of 'd' vs the result of the new expression 'd_wave'
d | d_wave |
- We want to reuse this script from VisIt's cli, so click 'Save Script' and save it to the file 'py_example_expression.py'.
Note: You can use the 'Load Script' button to load a filter script from a file or start with a simple template expression.
Example Script Breakdown
The meat of the new expression is in the 'derive_variable' function. It is called on each domain (or chunk) of your input dataset. The argument 'ds_in' is a python wrapped instance of vtkDataSet & 'domain_id' is an integer providing the id of the current domain.
Recall this script perturbs 'd' by simple wave pattern related, first we obtain the extents of the mesh:
ds_bounds = ds_in.GetBounds()
x_ext = ds_bounds[1] - ds_bounds[0]
y_ext = ds_bounds[3] - ds_bounds[2]
z_ext = ds_bounds[5] - ds_bounds[4]
Next we obtain the vtkDataArray containing the scalar values for the variable 'd':
cell_vals = ds_in.GetCellData().GetArray(self.input_var_names[0])
Notice we use the tuple 'self.input_var_names' to access the name of the variable we passed as an argument.
Construct an array to hold the results that has the same number cells as the input scalar:
ncells = ds_in.GetNumberOfCells()
res = vtk.vtkFloatArray()
res.SetNumberOfComponents(1)
res.SetNumberOfTuples(ncells)
Now scan over the cells and apply the wave pattern:
for i in xrange(ncells):
cell = ds_in.GetCell(i)
bounds = cell.GetBounds()
xv = bounds[0] + bounds[1] / 2.0
yv = bounds[2] + bounds[3] / 2.0
zv = bounds[4] + bounds[5] / 2.0
val = .05*sin(xv*3*pi/x_ext) + .05*sin(yv * 3*pi / y_ext)
if z_ext != 0:
val+= .05*sin(zv * 3*pi / z_ext)
val += cell_vals.GetTuple1(i)
res.SetTuple1(i,val)
return res
Finally we return the result array to VisIt:
return res
Another important detail is the assignment of 'py_filter':
py_filter = MyExpression
This tells the Python Filter Infrastructure which class to instantiate when the expression is run.