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