User:Tohline/IVAJ/Levels2and3
A Customized Python Module for CFD Flow Analysis within VisTrails
by Joel E. Tohline, Jinghua Ge, Wesley Even, & Erik Anderson
A relatively simple, customized Python module that plugs smoothly into an otherwise standard workflow within VisTrails facilitates a quantitative analysis of complex fluid flows in simulations of merging binary stars.
Introduction
Researchers in the open source community are steadily improving scientific visualization tools. These tools are providing a wider array of sophisticated probes for data analysis and a wider assortment of effective user-friendly interfaces. They're also making it easier for researchers in the computational science community – across many disciplines – to effectively analyze huge datasets by drawing on the human brain's acute ability to sort through complex and time-varying visual patterns. The astrophysics group at Louisiana State University (LSU), for example, routinely uses volume-rendering and ray-tracing algorithms in conjunction with animation techniques to examine the time-varying behavior of isodensity surfaces that arise in computational fluid dynamic (CFD) simulations of mass-transferring and merging binary star systems. Although such analyses generally provide only a qualitative identification and assessment of structure within a given dataset, the insight gained from visual inspection can nevertheless be extremely valuable. For example, it was through visual inspection that researchers at LSU initially spotted the nonlinear development of triangular-, square-, and pentagonal-shaped tidal resonances in recent simulations.
LSU's astrophysics group has begun to incorporate VisTrails into its arsenal of scientific visualization and data analysis tools. VisTrails primarily interested the group a few years ago because it provides a user-friendly workflow interface to the extensive VTK software library. It also automatically tracks the provenance of data analysis efforts. However, what most impresses us now is the ease with which VisTrails facilitates the insertion of home-grown analysis modules into an otherwise VTK-based workflow. Taking advantage of this additional programming versatility, we have gained a greater appreciation of the role that visualization tools can play in the quantitative assessment of results from large-scale simulations. In this article, we first describe the VTK-based workflow that we initially constructed in VisTrails to view streamlines within each binary mass-transfer simulation. We then describe the Python module, whose insertion into this workflow has permitted us to identify values of key rotational frequencies associated with such flows.
Aside
Before diving into a discussion of the VisTrails workflows that have been developed for this study, it will be useful to download the relevant .vt module from the VisTrails database.
<vistrail host="vistrails.sci.utah.edu" db="vistrails" vtid="19" version="1834" tag="" showspreadsheetonly="True"/>
Base Workflow
Within VisTrails, we initially selected various VTK-based modules to do the following, in sequence (see Figure 1a):
- Read simulation data. We used vtkPLOT3DReader to read in one file containing the <math>(x, y, z)</math> coordinate locations of every vertex on our 3D cylindrical coordinate mesh and a separate file containing the fluid's mass-density (scalar) and momentum-density (3D vector) at every grid vertex.
- Outline cylindrical domain boundary. As shown, we enlisted vtkStructuredGridOutlineFilter, vtkPolyDataMapper, and vtkActor.
- Define isodensity surfaces. We rendered two nested isodensity surfaces to outline high- (red) and low-density (blue) flow regions. The Red_contour and Blue_contour module groups each contain vtkContourFilter, vtkDataSetMapper, vtkProperty, and vtkActor.
- Draw streamlines. As Figure 1b shows, each of the eight separate Draw_Streamlines module groups uses vtkStreamLine, vtkTubeFilter, vtkDataSetMapper, vtkProperty, vtkActor, vtkSphereSource, vtkPolyDataMapper, and vtkLODActor to trace an individual streamline within the flow. Streamline lengths are set by feeding a common Propagation_Time into all eight module groups.
Vistrails renders the output from the various workflow actors in a composite scene using vtkRenderer as viewed by an observer located at a position that vtkCamera specifies. Finally, the module vtkCell directs this scene to the VisTrails interactive spreadsheet.
In this initially constructed base workflow, VisTrails pipes the 3D vector field representing the momentum density distribution from the vtkPLOT3DReader module directly into each of the eight Draw_Streamline module groups. This base workflow – which VisTrails assembles using generically available vtk modules – lets us examine the behavior of streamlines in our binary mass-transfer simulations, but only from the frame of reference, <math>\Omega_0</math>, in which we originally performed each simulation (see Figure 2b, labeled <math>\Delta\Omega = 0.00</math>).
Customized Python Module
import core.modules.module_registry from core.modules.vistrails_module import Module, ModuleError import vtk, math version="0.0.0" name="SwitchCoord" identifier="edu.lsu.switchcoord" class SwitchCoord(Module): """SwitchCoord let you transform between different coord systems""" def compute(self): minp = self.getInputFromPort("min_density") omega = self.getInputFromPort("omega") dataset=self.getInputFromPort("dataset") output = self.create_instance_of_type('edu.utah.sci.vistrails.vtk', 'vtkStructuredGrid') output.vtkInstance = vtk.vtkStructuredGrid() mydata=output.vtkInstance mydata.DeepCopy(dataset.vtkInstance) self.op(mydata, minp, omega) self.setResult("changed_dataset", output) def op(self, mydata, minp, omega): extent=mydata.GetExtent() print extent pcoords = mydata.GetPoints().GetData() density = mydata.GetPointData().GetScalars("Density") momentum = mydata.GetPointData().GetVectors("Momentum") #manipulate data test maxnorm = 0.0 for i in range(0, mydata.GetNumberOfPoints()): [x, y, z] = pcoords.GetTuple3(i) [_v1, _v2, _v3] = momentum.GetTuple3(i) p = density.GetValue(i) r = math.sqrt(x*x + y*y) phi = math.atan2(y, x) #if phi < 0: phi = 2*math.pi + phi #print "i, density, radius, phi", i, p, r, phi if p < minp: vx=vy=vz=0 else: vr = _v1 / p vphi = _v2 / (p) + r * omega vz = _v3 / p vx = vr * math.cos(phi) - vphi * math.sin(phi) vy = vr * math.sin(phi) + vphi * math.cos(phi) norm = math.sqrt(vx*vx + vy*vy + vz*vz) if norm > maxnorm: maxnorm = norm # vx = vx/norm # vy = vy/norm # vz = vz/norm momentum.SetTuple3(i, vx, vy, vz) print maxnorm for i in range(0, mydata.GetNumberOfPoints()): [vx, vy, vz] = momentum.GetTuple3(i) vx = vx/maxnorm vy = vy/maxnorm vz = vz/maxnorm momentum.SetTuple3(i, vx, vy, vz) def initialize(*args, **keywords): reg=core.modules.module_registry.registry reg.add_module(SwitchCoord) # reg.add_input_port(PythonCalc, "value1", # (core.modules.basic_modules.Float, 'the first argument')) # reg.add_input_port(PythonCalc, "value2", # (core.modules.basic_modules.Float, 'the second argument')) # reg.add_input_port(PythonCalc, "op", # (core.modules.basic_modules.String, 'the operation')) # reg.add_output_port(PythonCalc, "value", # (core.modules.basic_modules.Float, 'the result')) reg.add_input_port(SwitchCoord, "scalar_range", [core.modules.basic_modules.Float, core.modules.basic_modules.Float]) reg.add_input_port(SwitchCoord, "min_density", core.modules.basic_modules.Float) reg.add_input_port(SwitchCoord, "omega", core.modules.basic_modules.Float) reg.add_input_port(SwitchCoord, "dataset", (reg.get_descriptor_by_name( 'edu.utah.sci.vistrails.vtk', 'vtkStructuredGrid').module) ) reg.add_output_port(SwitchCoord, "changed_dataset", (reg.get_descriptor_by_name( 'edu.utah.sci.vistrails.vtk', 'vtkStructuredGrid').module) ) def package_dependencies(): import core.packagemanager manager = core.packagemanager.get_package_manager() if manager.has_package('edu.utah.sci.vistrails.vtk'): return ['edu.utah.sci.vistrails.vtk'] else: return []