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