1
2 """
3 Application class that implements pyFoamSurfacePlot.py
4 """
5
6 import sys,string
7 from os import path
8 from optparse import OptionGroup
9 from copy import copy
10 from math import ceil,sqrt
11
12 from PyFoamApplication import PyFoamApplication
13 from PyFoam.RunDictionary.SurfaceDirectory import SurfaceDirectory
14
15 from PyFoam.Error import error,warning
16
17 from PlotHelpers import cleanFilename
18
33
35 data=OptionGroup(self.parser,
36 "Data",
37 "Select the data to plot")
38 self.parser.add_option_group(data)
39
40 data.add_option("--surface",
41 action="append",
42 default=None,
43 dest="surface",
44 help="The sampled surface for which the data is plotted (can be used more than once)")
45 data.add_option("--field",
46 action="append",
47 default=None,
48 dest="field",
49 help="The fields that are plotted (can be used more than once). If none are specified all found fields are used")
50 data.add_option("--directory-name",
51 action="store",
52 default="surfaces",
53 dest="dirName",
54 help="Alternate name for the directory with the samples (Default: %default)")
55
56 time=OptionGroup(self.parser,
57 "Time",
58 "Select the times to plot")
59 self.parser.add_option_group(time)
60
61 time.add_option("--time",
62 action="append",
63 default=None,
64 dest="time",
65 help="The times that are plotted (can be used more than once). If none are specified all found times are used")
66 time.add_option("--min-time",
67 action="store",
68 type="float",
69 default=None,
70 dest="minTime",
71 help="The smallest time that should be used")
72 time.add_option("--max-time",
73 action="store",
74 type="float",
75 default=None,
76 dest="maxTime",
77 help="The biggest time that should be used")
78 time.add_option("--fuzzy-time",
79 action="store_true",
80 default=False,
81 dest="fuzzyTime",
82 help="Try to find the next timestep if the time doesn't match exactly")
83
84 output=OptionGroup(self.parser,
85 "Appearance",
86 "How it should be plotted")
87 self.parser.add_option_group(output)
88
89 output.add_option("--unscaled",
90 action="store_false",
91 dest="scaled",
92 default=True,
93 help="Don't scale a value to the same range for all plots")
94 output.add_option("--interpolate-to-point",
95 action="store_true",
96 dest="toPoint",
97 default=False,
98 help="Plot data interpolated to point values (although the real truth lies in the cells)")
99 output.add_option("--scale-all",
100 action="store_true",
101 dest="scaleAll",
102 default=False,
103 help="Use the same scale for all fields (else use one scale for each field)")
104 output.add_option("--picture-destination",
105 action="store",
106 dest="pictureDest",
107 default=None,
108 help="Directory the pictures should be stored to")
109 output.add_option("--name-prefix",
110 action="store",
111 dest="namePrefix",
112 default=None,
113 help="Prefix to the picture-name")
114 output.add_option("--clean-filename",
115 action="store_true",
116 dest="cleanFilename",
117 default=False,
118 help="Clean filenames so that they can be used in HTML or Latex-documents")
119
120 colorMaps=["blueToRed","redToBlue","blackToWhite","redToWhite"]
121
122
123 output.add_option("--color-map",
124 type="choice",
125 dest="colorMap",
126 default="blueToRed",
127 choices=colorMaps,
128 help="Sets the used colormap to one of "+string.join(colorMaps,", ")+" with the default: %default")
129
130 data.add_option("--info",
131 action="store_true",
132 dest="info",
133 default=False,
134 help="Print info about the sampled data and exit")
135
136 camera=OptionGroup(self.parser,
137 "Camera",
138 "How to look at things")
139 self.parser.add_option_group(camera)
140 camera.add_option("--no-auto-camera",
141 action="store_false",
142 dest="autoCamera",
143 default=True,
144 help="The position of the camera should not be determined automagically")
145 camera.add_option("--dolly-factor",
146 action="store",
147 dest="dollyFactor",
148 type="float",
149 default=1,
150 help="The dolly-factor used to focus the camera: %default")
151 camera.add_option("--width-of-bitmap",
152 action="store",
153 type="int",
154 dest="width",
155 default=720,
156 help="The width that the render-window should have. Default: %default")
157 camera.add_option("--height-of-bitmap",
158 action="store",
159 dest="height",
160 type="int",
161 default=None,
162 help="The height that the render-window should have. If unspecified it is determined from the size of the data")
163 camera.add_option("--focal-point-offset",
164 action="store",
165 dest="focalOffset",
166 default="0,0,0",
167 help="Offset of the focal point from the center of the data. Only used in manual-camera mode. Default: %default")
168 camera.add_option("--camera-offset",
169 action="store",
170 dest="cameraOffset",
171 default="0,0,1",
172 help="Offset of the position of the camera from the center of the data. Only used in manual-camera mode. Default: %default")
173 camera.add_option("--up-direction",
174 action="store",
175 dest="upDirection",
176 default="0,1,0",
177 help="Which direction is up. Only used in manual-camera mode. Default: %default")
178 camera.add_option("--report-camera",
179 action="store_true",
180 dest="reportCamera",
181 default=False,
182 help="Report the used settings for the camera")
183
184 behave=OptionGroup(self.parser,
185 "Behaviour",
186 "How the program affects its environment")
187 self.parser.add_option_group(behave)
188 behave.add_option("--offscreen",
189 action="store_true",
190 dest="offscreen",
191 default=False,
192 help="Try to render the image offscreen (without a window)")
193 behave.add_option("--silent",
194 action="store_true",
195 dest="silent",
196 default=False,
197 help="Don't write progress to the terminal")
198
200 if self.opts.offscreen:
201 grap=vtk.vtkGraphicsFactory()
202 grap.SetOffScreenOnlyMode(1)
203 grap.SetUseMesaClasses(1)
204 img=vtk.vtkImagingFactory()
205 img.SetUseMesaClasses(1)
206
207 self.reader = vtk.vtkDataSetReader()
208 self.reader.SetFileName(fName)
209 self.output = self.reader.GetOutput()
210 if self.opts.toPoint:
211 self.toPoint = vtk.vtkCellDataToPointData()
212 self.surfMapper = vtk.vtkDataSetMapper()
213 self.surfMapper.SetColorModeToMapScalars()
214 self.lut = vtk.vtkLookupTable()
215 if self.opts.colorMap=="blueToRed":
216 self.lut.SetHueRange(0.667,0)
217 elif self.opts.colorMap=="redToBlue":
218 self.lut.SetHueRange(0,0.667)
219 elif self.opts.colorMap=="blackToWhite":
220 self.lut.SetHueRange(1,1)
221 self.lut.SetValueRange(0,1)
222 self.lut.SetSaturationRange(0,0)
223 elif self.opts.colorMap=="redToWhite":
224 self.lut.SetHueRange(0,0.2)
225 self.lut.SetValueRange(1,1)
226 self.lut.SetSaturationRange(1,0.2)
227 else:
228 self.warning("Unknown colormap",self.opts.colorMap)
229
230 self.surfMapper.SetLookupTable(self.lut)
231 self.surfActor = vtk.vtkActor()
232 self.surfActor.SetMapper(self.surfMapper)
233 self.textActor = vtk.vtkTextActor()
234 self.textActor.SetDisplayPosition(90, 50)
235 self.textActor.SetTextScaleModeToViewport()
236 self.textActor.SetWidth(0.75)
237
238 self.barActor = vtk.vtkScalarBarActor()
239 self.barActor.SetLookupTable(self.lut)
240 self.barActor.SetDisplayPosition(90, 300)
241 self.barActor.SetOrientationToHorizontal()
242 self.barActor.SetHeight(0.15)
243 self.barActor.SetWidth(0.75)
244
245
246
247
248
249
250
251
252 self.ren = vtk.vtkRenderer()
253 self.renWin = vtk.vtkRenderWindow()
254 if self.opts.offscreen:
255 self.renWin.SetOffScreenRendering(1)
256 self.renWin.AddRenderer(self.ren)
257
258 self.ren.AddActor(self.surfActor)
259 self.ren.AddActor2D(self.textActor)
260 self.ren.AddActor2D(self.barActor)
261
262
263
264
265 self.ren.SetBackground(0.7, 0.7, 0.7)
266
267 self.hasPipeline=True
268
270 if not self.hasPipeline:
271 self.setupPipeline(fName)
272 else:
273 self.reader.SetFileName(fName)
274
275 self.reader.Update()
276 self.output = self.reader.GetOutput()
277 if self.opts.toPoint:
278 self.toPoint.SetInput(self.output)
279 self.surfMapper.SetInput(self.toPoint.GetOutput())
280 else:
281 self.surfMapper.SetInput(self.output)
282 self.cData=self.output.GetCellData()
283 self.cData.SetScalars(self.cData.GetArray(0))
284 self.surfMapper.SetScalarRange(self.reader.GetOutput().GetScalarRange())
285
287 self.surfMapper.SetScalarRange(rng)
288
290 self.textActor.SetInput(title)
291 self.barActor.SetTitle(bar)
292
294 return self.reader.GetOutput().GetScalarRange()
295
297 return map(float,opt.split(','))
298
300 self.ren.ResetCamera()
301
302 xMin,xMax,yMin,yMax,zMin,zMax=self.output.GetBounds()
303
304 boundRange=[(xMax-xMin,0),
305 (yMax-yMin,1),
306 (zMax-zMin,2)]
307 boundRange.sort(lambda a,b:cmp(b[0],a[0]))
308 focalPoint=[0.5*(xMax+xMin),0.5*(yMax+yMin),0.5*(zMax+zMin)]
309 position=copy(focalPoint)
310 if self.opts.autoCamera:
311 ratio=max(0.2,boundRange[1][0]/max(boundRange[0][0],1e-10))
312 self.opts.height=int(self.opts.width*ratio)+70
313 camOffset=[0,0,0]
314 camOffset[boundRange[2][1]]=boundRange[1][0]*3
315 up=[0,0,0]
316 up[boundRange[1][1]]=1.
317 else:
318 if self.opts.height==None:
319 self.opts.height=int(self.opts.width/sqrt(2))
320 offset=self.getVector(self.opts.focalOffset)
321 for i in range(3):
322 focalPoint[i]+=offset[i]
323 camOffset=self.getVector(self.opts.cameraOffset)
324 up=self.getVector(self.opts.upDirection)
325
326 for i in range(3):
327 position[i]+=camOffset[i]
328
329 if self.opts.reportCamera:
330 print "Picture size:",self.opts.width,self.opts.height
331 print "Data bounds:",xMin,xMax,yMin,yMax,zMin,zMax
332 print "Focal point:",focalPoint
333 print "Up-direction:",up
334 print "Camera position:",position
335
336 self.renWin.SetSize(self.opts.width,self.opts.height)
337 self.barActor.SetDisplayPosition(int(self.opts.width*0.124), 20)
338 self.textActor.SetDisplayPosition(int(self.opts.width*0.124),self.opts.height-30)
339 self.ren.GetActiveCamera().SetFocalPoint(focalPoint)
340 self.ren.GetActiveCamera().SetViewUp(up)
341 self.ren.GetActiveCamera().SetPosition(position)
342 self.ren.GetActiveCamera().Dolly(self.opts.dollyFactor)
343 self.ren.ResetCameraClippingRange()
344
345 self.renWin.Render()
346
347 self.renderLarge = vtk.vtkRenderLargeImage()
348 self.renderLarge.SetInput(self.ren)
349 self.renderLarge.SetMagnification(1)
350
351 self.writer = vtk.vtkPNGWriter()
352 self.writer.SetInputConnection(self.renderLarge.GetOutputPort())
353
354 self.writer.SetFileName(fName)
355 self.writer.Write()
356
358 global vtk
359 import vtk
360
361 caseName=path.basename(path.abspath(self.parser.getArgs()[0]))
362 samples=SurfaceDirectory(self.parser.getArgs()[0],dirName=self.opts.dirName)
363 self.hasPipeline=False
364
365 if self.opts.info:
366 print "Times : ",samples.times
367 print "Surfaces : ",samples.surfaces()
368 print "Values : ",samples.values()
369 sys.exit(0)
370
371 surfaces=samples.surfaces()
372 times=samples.times
373 values=samples.values()
374
375 if self.opts.surface==None:
376
377 self.opts.surface=surfaces
378 else:
379 for l in self.opts.surface:
380 if l not in surfaces:
381 error("The line",l,"does not exist in",lines)
382
383 if self.opts.maxTime or self.opts.minTime:
384 if self.opts.time:
385 error("Times",self.opts.time,"and range [",self.opts.minTime,",",self.opts.maxTime,"] set: contradiction")
386 self.opts.time=[]
387 if self.opts.maxTime==None:
388 self.opts.maxTime= 1e20
389 if self.opts.minTime==None:
390 self.opts.minTime=-1e20
391
392 for t in times:
393 if float(t)<=self.opts.maxTime and float(t)>=self.opts.minTime:
394 self.opts.time.append(t)
395
396 if len(self.opts.time)==0:
397 error("No times in range [",self.opts.minTime,",",self.opts.maxTime,"] found: ",times)
398 elif self.opts.time:
399 iTimes=self.opts.time
400 self.opts.time=[]
401 for t in iTimes:
402 if t in samples.times:
403 self.opts.time.append(t)
404 elif self.opts.fuzzyTime:
405 tf=float(t)
406 use=None
407 dist=1e20
408 for ts in samples.times:
409 if abs(tf-float(ts))<dist:
410 use=ts
411 dist=abs(tf-float(ts))
412 if use and use not in self.opts.time:
413 self.opts.time.append(use)
414 else:
415 self.warning("Time",t,"not found in the sample-times. Use option --fuzzy")
416
417 if not self.opts.silent:
418 print "Getting data about plots"
419 plots=[]
420
421 if self.opts.time==None:
422 self.opts.time=samples.times
423 elif len(self.opts.time)==0:
424 self.error("No times specified. Exiting")
425 if self.opts.field==None:
426 self.opts.field=samples.values()
427 for s in self.opts.surface:
428 for t in self.opts.time:
429 for f in self.opts.field:
430 plt=samples.getData(surface=[s],
431 value=[f],
432 time=[t])
433 if plt:
434 plots+=plt
435
436 vRanges=None
437 if self.opts.scaled:
438 if not self.opts.silent:
439 print "Getting ranges"
440 if self.opts.scaleAll:
441 vRange=None
442 else:
443 vRanges={}
444
445 for p in plots:
446 f,tm,surf,nm=p
447 self.setFilename(f)
448
449 mi,ma=self.getCurrentRange()
450 if not self.opts.scaleAll:
451 if nm in vRanges:
452 vRange=vRanges[nm]
453 else:
454 vRange=None
455
456 if vRange==None:
457 vRange=mi,ma
458 else:
459 vRange=min(vRange[0],mi),max(vRange[1],ma)
460 if not self.opts.scaleAll:
461 vRanges[nm]=vRange
462
463 for p in plots:
464 f,time,surf,nm=p
465
466 name=""
467 if self.opts.namePrefix:
468 name+=self.opts.namePrefix+"_"
469 name+=self.opts.dirName
470 tIndex=times.index(time)
471
472 name+="_"+surf
473
474 name+="_%s_%04d" % (nm,tIndex)
475 title="%s : %s - %s t=%f" % (caseName,self.opts.dirName,surf,float(time))
476
477 name+=".png"
478 if self.opts.cleanFilename:
479 name=cleanFilename(name)
480
481 if self.opts.pictureDest:
482 name=path.join(self.opts.pictureDest,name)
483
484 self.setFilename(f)
485 if self.opts.scaleAll:
486 if vRange:
487 self.setRange(vRange)
488 else:
489 if vRanges:
490 if nm in vRanges:
491 self.setRange(vRanges[nm])
492
493 self.setTitles(title,nm)
494
495 if not self.opts.silent:
496 print "Writing picture",name
497
498 self.writePicture(name)
499