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.

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

  1. Run visit
  2. Open wave.visit or wave*.silo database files
  3. Add a Pseudocolor plot of pressure
  4. Draw the plots
  5. Open the Controls->Command window
  6. Paste the script into tab 1 and click the Execute button. This will launch VisIt's Python interface and install the Python callback function
  7. 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.
  8. 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.

Pclimits.png

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

  1. Copy the script to a file called rmpickcells.py
  2. Run visit -cli -s rmpickcells.py -o example.silo
  3. Add a Pseudocolor plot of temp_slice by typing AddPlot("Pseudocolor", "temp_slice")
  4. Draw the plots by typing DrawPlots()
  5. Click on the zone-pick icon in the vis window
  6. Click on various cells in the plots and watch them disappear

RemovePickCells.png

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:
Py filt ex expr 1.png
  • 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.
Py filt ex expr 2.png
  • 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
Py filt ex expr 3.png
Py filt ex expr 4.png


  • 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.