Source code for PyFoam.Applications.SurfacePlot

#  ICE Revision: $Id$
"""
Application class that implements pyFoamSurfacePlot.py
"""

import sys,string
from os import path
from optparse import OptionGroup
from copy import copy
from math import sqrt

from .PyFoamApplication import PyFoamApplication
from PyFoam.RunDictionary.SurfaceDirectory import SurfaceDirectory

from PyFoam.Error import error

from .PlotHelpers import cleanFilename

from PyFoam.ThirdParty.six import print_
from PyFoam.ThirdParty.six.moves import input

[docs]class SurfacePlot(PyFoamApplication): def __init__(self, args=None, **kwargs): description="""\ Searches for sampled surface in the VTK-format in a directory and makes pictures from them """ PyFoamApplication.__init__(self, args=args, description=description, usage="%prog [options] <casedir>", nr=1, changeVersion=False, interspersed=True, **kwargs)
[docs] def addOptions(self): data=OptionGroup(self.parser, "Data", "Select the data to plot") self.parser.add_option_group(data) data.add_option("--surface", action="append", default=None, dest="surface", help="The sampled surface for which the data is plotted (can be used more than once)") data.add_option("--field", action="append", default=None, dest="field", help="The fields that are plotted (can be used more than once). If none are specified all found fields are used") data.add_option("--directory-name", action="store", default="surfaces", dest="dirName", help="Alternate name for the directory with the samples (Default: %default)") time=OptionGroup(self.parser, "Time", "Select the times to plot") self.parser.add_option_group(time) time.add_option("--time", action="append", default=None, dest="time", help="The times that are plotted (can be used more than once). If none are specified all found times are used") time.add_option("--min-time", action="store", type="float", default=None, dest="minTime", help="The smallest time that should be used") time.add_option("--max-time", action="store", type="float", default=None, dest="maxTime", help="The biggest time that should be used") time.add_option("--fuzzy-time", action="store_true", default=False, dest="fuzzyTime", help="Try to find the next timestep if the time doesn't match exactly") output=OptionGroup(self.parser, "Appearance", "How it should be plotted") self.parser.add_option_group(output) output.add_option("--unscaled", action="store_false", dest="scaled", default=True, help="Don't scale a value to the same range for all plots") output.add_option("--interpolate-to-point", action="store_true", dest="toPoint", default=False, help="Plot data interpolated to point values (although the real truth lies in the cells)") output.add_option("--scale-all", action="store_true", dest="scaleAll", default=False, help="Use the same scale for all fields (else use one scale for each field)") output.add_option("--picture-destination", action="store", dest="pictureDest", default=None, help="Directory the pictures should be stored to") output.add_option("--name-prefix", action="store", dest="namePrefix", default=None, help="Prefix to the picture-name") output.add_option("--clean-filename", action="store_true", dest="cleanFilename", default=False, help="Clean filenames so that they can be used in HTML or Latex-documents") colorMaps=["blueToRed","redToBlue","blackToWhite","redToWhite"] # colorMaps.append("experimental") output.add_option("--color-map", type="choice", dest="colorMap", default="blueToRed", choices=colorMaps, help="Sets the used colormap to one of "+", ".join(colorMaps)+" with the default: %default") data.add_option("--info", action="store_true", dest="info", default=False, help="Print info about the sampled data and exit") camera=OptionGroup(self.parser, "Camera", "How to look at things") self.parser.add_option_group(camera) camera.add_option("--no-auto-camera", action="store_false", dest="autoCamera", default=True, help="The position of the camera should not be determined automagically") camera.add_option("--dolly-factor", action="store", dest="dollyFactor", type="float", default=1, help="The dolly-factor used to focus the camera: %default") camera.add_option("--width-of-bitmap", action="store", type="int", dest="width", default=720, help="The width that the render-window should have. Default: %default") camera.add_option("--height-of-bitmap", action="store", dest="height", type="int", default=None, help="The height that the render-window should have. If unspecified it is determined from the size of the data") camera.add_option("--focal-point-offset", action="store", dest="focalOffset", default="0,0,0", help="Offset of the focal point from the center of the data. Only used in manual-camera mode. Default: %default") camera.add_option("--camera-offset", action="store", dest="cameraOffset", default="0,0,1", help="Offset of the position of the camera from the center of the data. Only used in manual-camera mode. Default: %default") camera.add_option("--up-direction", action="store", dest="upDirection", default="0,1,0", help="Which direction is up. Only used in manual-camera mode. Default: %default") camera.add_option("--report-camera", action="store_true", dest="reportCamera", default=False, help="Report the used settings for the camera") behave=OptionGroup(self.parser, "Behaviour", "How the program affects its environment") self.parser.add_option_group(behave) behave.add_option("--offscreen", action="store_true", dest="offscreen", default=False, help="Try to render the image offscreen (without a window)") behave.add_option("--silent", action="store_true", dest="silent", default=False, help="Don't write progress to the terminal") behave.add_option("--wait", action="store_true", dest="wait", default=False, help="Keep window open until a key is pressed")
[docs] def setupPipeline(self,fName): if self.opts.offscreen: grap=vtk.vtkGraphicsFactory() grap.SetOffScreenOnlyMode(1) grap.SetUseMesaClasses(1) img=vtk.vtkImagingFactory() img.SetUseMesaClasses(1) self.reader = vtk.vtkDataSetReader() self.reader.SetFileName(fName) self.output = self.reader.GetOutput() if self.opts.toPoint: self.toPoint = vtk.vtkCellDataToPointData() self.surfMapper = vtk.vtkDataSetMapper() self.surfMapper.SetColorModeToMapScalars() self.lut = vtk.vtkLookupTable() if self.opts.colorMap=="blueToRed": self.lut.SetHueRange(0.667,0) elif self.opts.colorMap=="redToBlue": self.lut.SetHueRange(0,0.667) elif self.opts.colorMap=="blackToWhite": self.lut.SetHueRange(1,1) self.lut.SetValueRange(0,1) self.lut.SetSaturationRange(0,0) elif self.opts.colorMap=="redToWhite": self.lut.SetHueRange(0,0.2) self.lut.SetValueRange(1,1) self.lut.SetSaturationRange(1,0.2) else: self.warning("Unknown colormap",self.opts.colorMap) self.surfMapper.SetLookupTable(self.lut) self.surfActor = vtk.vtkActor() self.surfActor.SetMapper(self.surfMapper) self.textActor = vtk.vtkTextActor() self.textActor.SetDisplayPosition(90, 50) self.textActor.SetTextScaleModeToViewport() self.textActor.SetWidth(0.75) self.barActor = vtk.vtkScalarBarActor() self.barActor.SetLookupTable(self.lut) self.barActor.SetDisplayPosition(90, 300) self.barActor.SetOrientationToHorizontal() self.barActor.SetHeight(0.15) self.barActor.SetWidth(0.75) # self.axes=vtk.vtkCubeAxesActor() # self.axes.SetFlyModeToClosestTriad() # self.axes.SetCornerOffset(0.1) # self.axes.SetXLabelFormat("%6.1f") # self.axes.SetYLabelFormat("%6.1f") # self.axes.SetZLabelFormat("%6.1f") # Create graphics stuff self.ren = vtk.vtkRenderer() self.renWin = vtk.vtkRenderWindow() if self.opts.offscreen: self.renWin.SetOffScreenRendering(1) self.renWin.AddRenderer(self.ren) self.ren.AddActor(self.surfActor) self.ren.AddActor2D(self.textActor) self.ren.AddActor2D(self.barActor) # self.ren.AddViewProp(self.axes) # self.axes.SetCamera(self.ren.GetActiveCamera()) self.ren.SetBackground(0.7, 0.7, 0.7) self.hasPipeline=True
[docs] def setFilename(self,fName): if not self.hasPipeline: self.setupPipeline(fName) else: self.reader.SetFileName(fName) self.reader.Update() self.output = self.reader.GetOutput() if self.opts.toPoint: self.toPoint.SetInput(self.output) self.surfMapper.SetInput(self.toPoint.GetOutput()) else: self.surfMapper.SetInput(self.output) self.cData=self.output.GetCellData() self.cData.SetScalars(self.cData.GetArray(0)) self.surfMapper.SetScalarRange(self.reader.GetOutput().GetScalarRange())
[docs] def setRange(self,rng): self.surfMapper.SetScalarRange(rng)
[docs] def setTitles(self,title,bar): self.textActor.SetInput(title) self.barActor.SetTitle(bar)
[docs] def getCurrentRange(self): return self.reader.GetOutput().GetScalarRange()
[docs] def getVector(self,opt): return list(map(float,opt.split(',')))
[docs] def writePicture(self,fName): self.ren.ResetCamera() xMin,xMax,yMin,yMax,zMin,zMax=self.output.GetBounds() # self.axes.SetBounds(self.output.GetBounds()) boundRange=[(xMax-xMin,0), (yMax-yMin,1), (zMax-zMin,2)] boundRange.sort(key=lambda a:a[0],reverse=True) focalPoint=[0.5*(xMax+xMin),0.5*(yMax+yMin),0.5*(zMax+zMin)] position=copy(focalPoint) if self.opts.autoCamera: ratio=max(0.2,boundRange[1][0]/max(boundRange[0][0],1e-10)) self.opts.height=int(self.opts.width*ratio)+70 camOffset=[0,0,0] camOffset[boundRange[2][1]]=boundRange[1][0]*3 up=[0,0,0] up[boundRange[1][1]]=1. else: if self.opts.height==None: self.opts.height=int(self.opts.width/sqrt(2)) offset=self.getVector(self.opts.focalOffset) for i in range(3): focalPoint[i]+=offset[i] camOffset=self.getVector(self.opts.cameraOffset) up=self.getVector(self.opts.upDirection) for i in range(3): position[i]+=camOffset[i] if self.opts.reportCamera: print_("Picture size:",self.opts.width,self.opts.height) print_("Data bounds:",xMin,xMax,yMin,yMax,zMin,zMax) print_("Focal point:",focalPoint) print_("Up-direction:",up) print_("Camera position:",position) self.renWin.SetSize(self.opts.width,self.opts.height) self.barActor.SetDisplayPosition(int(self.opts.width*0.124), 20) self.textActor.SetDisplayPosition(int(self.opts.width*0.124),self.opts.height-30) self.ren.GetActiveCamera().SetFocalPoint(focalPoint) self.ren.GetActiveCamera().SetViewUp(up) self.ren.GetActiveCamera().SetPosition(position) self.ren.GetActiveCamera().Dolly(self.opts.dollyFactor) self.ren.ResetCameraClippingRange() self.renWin.Render() self.renderLarge = vtk.vtkRenderLargeImage() self.renderLarge.SetInput(self.ren) self.renderLarge.SetMagnification(1) self.writer = vtk.vtkPNGWriter() self.writer.SetInputConnection(self.renderLarge.GetOutputPort()) self.writer.SetFileName(fName) self.writer.Write() if self.opts.wait: input("waiting for key")
[docs] def run(self): global vtk import vtk caseName=path.basename(path.abspath(self.parser.getArgs()[0])) samples=SurfaceDirectory(self.parser.getArgs()[0],dirName=self.opts.dirName) self.hasPipeline=False if self.opts.info: print_("Times : ",samples.times) print_("Surfaces : ",samples.surfaces()) print_("Values : ",list(samples.values())) sys.exit(0) surfaces=samples.surfaces() times=samples.times values=list(samples.values()) if self.opts.surface==None: # error("At least one line has to be specified. Found were",samples.lines()) self.opts.surface=surfaces else: for l in self.opts.surface: if l not in surfaces: error("The line",l,"does not exist in",lines) if self.opts.maxTime or self.opts.minTime: if self.opts.time: error("Times",self.opts.time,"and range [",self.opts.minTime,",",self.opts.maxTime,"] set: contradiction") self.opts.time=[] if self.opts.maxTime==None: self.opts.maxTime= 1e20 if self.opts.minTime==None: self.opts.minTime=-1e20 for t in times: if float(t)<=self.opts.maxTime and float(t)>=self.opts.minTime: self.opts.time.append(t) if len(self.opts.time)==0: error("No times in range [",self.opts.minTime,",",self.opts.maxTime,"] found: ",times) elif self.opts.time: iTimes=self.opts.time self.opts.time=[] for t in iTimes: if t in samples.times: self.opts.time.append(t) elif self.opts.fuzzyTime: tf=float(t) use=None dist=1e20 for ts in samples.times: if abs(tf-float(ts))<dist: use=ts dist=abs(tf-float(ts)) if use and use not in self.opts.time: self.opts.time.append(use) else: self.warning("Time",t,"not found in the sample-times. Use option --fuzzy") if not self.opts.silent: print_("Getting data about plots") plots=[] if self.opts.time==None: self.opts.time=samples.times elif len(self.opts.time)==0: self.error("No times specified. Exiting") if self.opts.field==None: self.opts.field=list(samples.values()) for s in self.opts.surface: for t in self.opts.time: for f in self.opts.field: plt=samples.getData(surface=[s], value=[f], time=[t]) if plt: plots+=plt vRanges=None if self.opts.scaled: if not self.opts.silent: print_("Getting ranges") if self.opts.scaleAll: vRange=None else: vRanges={} for p in plots: f,tm,surf,nm=p self.setFilename(f) mi,ma=self.getCurrentRange() if not self.opts.scaleAll: if nm in vRanges: vRange=vRanges[nm] else: vRange=None if vRange==None: vRange=mi,ma else: vRange=min(vRange[0],mi),max(vRange[1],ma) if not self.opts.scaleAll: vRanges[nm]=vRange for p in plots: f,time,surf,nm=p name="" if self.opts.namePrefix: name+=self.opts.namePrefix+"_" name+=self.opts.dirName tIndex=times.index(time) name+="_"+surf name+="_%s_%04d" % (nm,tIndex) title="%s : %s - %s t=%f" % (caseName,self.opts.dirName,surf,float(time)) name+=".png" if self.opts.cleanFilename: name=cleanFilename(name) if self.opts.pictureDest: name=path.join(self.opts.pictureDest,name) self.setFilename(f) if self.opts.scaleAll: if vRange: self.setRange(vRange) else: if vRanges: if nm in vRanges: self.setRange(vRanges[nm]) self.setTitles(title,nm) if not self.opts.silent: print_("Writing picture",name) self.writePicture(name)
# Should work with Python3 and Python2