Package PyFoam :: Package Applications :: Module TimelinePlot
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Applications.TimelinePlot

  1  #  ICE Revision: $Id: /local/openfoam/Python/PyFoam/PyFoam/Applications/TimelinePlot.py 8478 2013-10-03T16:42:16.004565Z bgschaid  $ 
  2  """ 
  3  Application class that implements pyFoamTimelinePlot.py 
  4  """ 
  5   
  6  import sys 
  7  from os import path 
  8  from optparse import OptionGroup 
  9   
 10  from .PyFoamApplication import PyFoamApplication 
 11  from PyFoam.RunDictionary.TimelineDirectory import TimelineDirectory 
 12  from PyFoam.Basics.SpreadsheetData import WrongDataSize 
 13  from PyFoam.ThirdParty.six import print_ 
 14   
 15  from .PlotHelpers import cleanFilename 
 16   
17 -class TimelinePlot(PyFoamApplication):
18 - def __init__(self,args=None):
19 description="""\ 20 Searches a directory for timelines that were generated by some 21 functionObject and generates the commands to gnuplot it. As an option 22 the data can be written to a CSV-file. 23 """ 24 25 PyFoamApplication.__init__(self, 26 args=args, 27 description=description, 28 usage="%prog [options] <casedir>", 29 nr=1, 30 changeVersion=False, 31 interspersed=True)
32
33 - def addOptions(self):
34 data=OptionGroup(self.parser, 35 "Data", 36 "Select the data to plot") 37 self.parser.add_option_group(data) 38 39 data.add_option("--fields", 40 action="append", 41 default=None, 42 dest="fields", 43 help="The fields for which timelines should be plotted. All if unset") 44 data.add_option("--positions", 45 action="append", 46 default=None, 47 dest="positions", 48 help="The positions for which timelines should be plotted. Either strings or integers (then the corresponding column number will be used). All if unset") 49 data.add_option("--write-time", 50 default=None, 51 dest="writeTime", 52 help="If more than one time-subdirectory is stored select which one is used") 53 data.add_option("--directory-name", 54 action="store", 55 default="probes", 56 dest="dirName", 57 help="Alternate name for the directory with the samples (Default: %default)") 58 data.add_option("--reference-directory", 59 action="store", 60 default=None, 61 dest="reference", 62 help="A reference directory. If fitting timeline data is found there it is plotted alongside the regular data") 63 data.add_option("--reference-case", 64 action="store", 65 default=None, 66 dest="referenceCase", 67 help="A reference case where a directory with the same name is looked for. Mutual exclusive with --reference-directory") 68 69 time=OptionGroup(self.parser, 70 "Time", 71 "Select the times to plot") 72 self.parser.add_option_group(time) 73 74 time.add_option("--time", 75 action="append", 76 type="float", 77 default=None, 78 dest="time", 79 help="The times that are plotted (can be used more than once). Has to be specified for bars") 80 time.add_option("--min-time", 81 action="store", 82 type="float", 83 default=None, 84 dest="minTime", 85 help="The smallest time that should be used for lines") 86 time.add_option("--max-time", 87 action="store", 88 type="float", 89 default=None, 90 dest="maxTime", 91 help="The biggest time that should be used for lines") 92 time.add_option("--reference-time", 93 action="store_true", 94 default=False, 95 dest="referenceTime", 96 help="Use the time of the reference data for scaling instead of the regular data") 97 98 99 plot=OptionGroup(self.parser, 100 "Plot", 101 "How data should be plotted") 102 self.parser.add_option_group(plot) 103 104 plot.add_option("--basic-mode", 105 type="choice", 106 dest="basicMode", 107 default=None, 108 choices=["bars","lines"], 109 help="Whether 'bars' of the values at selected times or 'lines' over the whole timelines should be plotted") 110 vModes=["mag","x","y","z"] 111 plot.add_option("--vector-mode", 112 type="choice", 113 dest="vectorMode", 114 default="mag", 115 choices=vModes, 116 help="How vectors should be plotted. By magnitude or as a component. Possible values are "+str(vModes)+" Default: %default") 117 plot.add_option("--collect-lines-by", 118 type="choice", 119 dest="collectLines", 120 default="fields", 121 choices=["fields","positions"], 122 help="Collect lines for lineplotting either by 'fields' or 'positions'. Default: %default") 123 124 output=OptionGroup(self.parser, 125 "Output", 126 "Where data should be plotted to") 127 self.parser.add_option_group(output) 128 129 output.add_option("--gnuplot-file", 130 action="store", 131 dest="gnuplotFile", 132 default=None, 133 help="Write the necessary gnuplot commands to this file. Else they are written to the standard output") 134 output.add_option("--picture-destination", 135 action="store", 136 dest="pictureDest", 137 default=None, 138 help="Directory the pictures should be stored to") 139 output.add_option("--name-prefix", 140 action="store", 141 dest="namePrefix", 142 default=None, 143 help="Prefix to the picture-name") 144 output.add_option("--clean-filename", 145 action="store_true", 146 dest="cleanFilename", 147 default=False, 148 help="Clean filenames so that they can be used in HTML or Latex-documents") 149 output.add_option("--csv-file", 150 action="store", 151 dest="csvFile", 152 default=None, 153 help="Write the data to a CSV-file instead of the gnuplot-commands") 154 output.add_option("--excel-file", 155 action="store", 156 dest="excelFile", 157 default=None, 158 help="Write the data to a Excel-file instead of the gnuplot-commands") 159 output.add_option("--pandas-data", 160 action="store_true", 161 dest="pandasData", 162 default=False, 163 help="Pass the raw data in pandas-format") 164 output.add_option("--numpy-data", 165 action="store_true", 166 dest="numpyData", 167 default=False, 168 help="Pass the raw data in numpy-format") 169 output.add_option("--reference-prefix", 170 action="store", 171 dest="refprefix", 172 default="Reference", 173 help="Prefix that gets added to the reference lines. Default: %default") 174 175 data.add_option("--info", 176 action="store_true", 177 dest="info", 178 default=False, 179 help="Print info about the sampled data and exit") 180 output.add_option("--resample", 181 action="store_true", 182 dest="resample", 183 default=False, 184 help="Resample the reference value to the current x-axis (for CSV and Excel-output)") 185 output.add_option("--extend-data", 186 action="store_true", 187 dest="extendData", 188 default=False, 189 help="Extend the data range if it differs (for CSV and Excel-files)") 190 output.add_option("--silent", 191 action="store_true", 192 dest="silent", 193 default=False, 194 help="Don't write to screen (with the silent and the compare-options)") 195 196 numerics=OptionGroup(self.parser, 197 "Quantify", 198 "Metrics of the data and numerical comparisons") 199 self.parser.add_option_group(numerics) 200 numerics.add_option("--compare", 201 action="store_true", 202 dest="compare", 203 default=None, 204 help="Compare all data sets that are also in the reference data") 205 numerics.add_option("--metrics", 206 action="store_true", 207 dest="metrics", 208 default=None, 209 help="Print the metrics of the data sets") 210 numerics.add_option("--use-reference-for-comparison", 211 action="store_false", 212 dest="compareOnOriginal", 213 default=True, 214 help="Use the reference-data as the basis for the numerical comparison. Otherwise the original data will be used")
215
216 - def setFile(self,fName):
217 if self.opts.namePrefix: 218 fName=self.opts.namePrefix+"_"+fName 219 if self.opts.pictureDest: 220 fName=path.join(self.opts.pictureDest,fName) 221 222 name=fName 223 if self.opts.cleanFilename: 224 name=cleanFilename(fName) 225 return 'set output "%s"\n' % name
226
227 - def run(self):
228 # remove trailing slashif present 229 if self.opts.dirName[-1]==path.sep: 230 self.opts.dirName=self.opts.dirName[:-1] 231 232 usedDirName=self.opts.dirName.replace("/","_") 233 234 timelines=TimelineDirectory(self.parser.getArgs()[0], 235 dirName=self.opts.dirName, 236 writeTime=self.opts.writeTime) 237 reference=None 238 if self.opts.reference and self.opts.referenceCase: 239 self.error("Options --reference-directory and --reference-case are mutual exclusive") 240 if (self.opts.csvFile or self.opts.excelFile or self.opts.pandasData or self.opts.numpyData) and (self.opts.compare or self.opts.metrics): 241 self.error("Options --csv-file/excel-file/--pandas-data/--numpy-data and --compare/--metrics are mutual exclusive") 242 243 if self.opts.reference: 244 reference=TimelineDirectory(self.parser.getArgs()[0], 245 dirName=self.opts.reference, 246 writeTime=self.opts.writeTime) 247 elif self.opts.referenceCase: 248 reference=TimelineDirectory(self.opts.referenceCase, 249 dirName=self.opts.dirName, 250 writeTime=self.opts.writeTime) 251 252 if self.opts.info: 253 self.setData({'writeTimes' : timelines.writeTimes, 254 'usedTimes' : timelines.usedTime, 255 'fields' : timelines.values, 256 'positions' : timelines.positions(), 257 'timeRange' : timelines.timeRange()}) 258 259 if not self.opts.silent: 260 print_("Write Times : ",timelines.writeTimes) 261 print_("Used Time : ",timelines.usedTime) 262 print_("Fields : ",timelines.values,end="") 263 if len(timelines.vectors)>0: 264 if not self.opts.silent: 265 print_(" Vectors: ",timelines.vectors) 266 self.setData({'vectors':timelines.vectors}) 267 else: 268 if not self.opts.silent: 269 print_() 270 if not self.opts.silent: 271 print_("Positions : ",timelines.positions()) 272 print_("Time range : ",timelines.timeRange()) 273 274 if reference: 275 refData={'writeTimes' : reference.writeTimes, 276 'fields' : reference.values, 277 'positions' : reference.positions(), 278 'timeRange' : reference.timeRange()} 279 280 if not self.opts.silent: 281 print_("\nReference Data") 282 print_("Write Times : ",reference.writeTimes) 283 print_("Fields : ",reference.values,end="") 284 if len(reference.vectors)>0: 285 if not self.opts.silent: 286 print_(" Vectors: ",reference.vectors) 287 refData["vectors"]=reference.vectors 288 else: 289 if not self.opts.silent: 290 print_() 291 if not self.opts.silent: 292 print_("Positions : ",reference.positions()) 293 print_("Time range : ",reference.timeRange()) 294 self.setData({"reference":refData}) 295 296 return 0 297 298 if self.opts.fields==None: 299 self.opts.fields=timelines.values 300 else: 301 for v in self.opts.fields: 302 if v not in timelines.values: 303 self.error("The requested value",v,"not in possible values",timelines.values) 304 if self.opts.positions==None: 305 self.opts.positions=timelines.positions() 306 else: 307 pos=self.opts.positions 308 self.opts.positions=[] 309 for p in pos: 310 try: 311 p=int(p) 312 if p<0 or p>=len(timelines.positions()): 313 self.error("Time index",p,"out of range for positons",timelines.positions()) 314 else: 315 self.opts.positions.append(timelines.positions()[p]) 316 except ValueError: 317 if p not in timelines.positions(): 318 self.error("Position",p,"not in",timelines.positions()) 319 else: 320 self.opts.positions.append(p) 321 322 if len(self.opts.positions)==0: 323 self.error("No valid positions") 324 325 result="set term png nocrop enhanced \n" 326 327 if self.opts.basicMode==None: 328 self.error("No mode selected. Do so with '--basic-mode'") 329 elif self.opts.basicMode=='bars': 330 if self.opts.time==None: 331 self.error("No times specified for bar-plots") 332 self.opts.time.sort() 333 if self.opts.referenceTime and reference!=None: 334 minTime,maxTime=reference.timeRange() 335 else: 336 minTime,maxTime=timelines.timeRange() 337 usedTimes=[] 338 hasMin=False 339 for t in self.opts.time: 340 if t<minTime: 341 if not hasMin: 342 usedTimes.append(minTime) 343 hasMin=True 344 elif t>maxTime: 345 usedTimes.append(maxTime) 346 break 347 else: 348 usedTimes.append(t) 349 data=timelines.getData(usedTimes, 350 value=self.opts.fields, 351 position=self.opts.positions, 352 vectorMode=self.opts.vectorMode) 353 # print_(data) 354 result+="set style data histogram\n" 355 result+="set style histogram cluster gap 1\n" 356 result+="set style fill solid border -1\n" 357 result+="set boxwidth 0.9\n" 358 result+="set xtics border in scale 1,0.5 nomirror rotate by 90 offset character 0, 0, 0\n" 359 # set xtic rotate by -45\n" 360 result+="set xtics (" 361 for i,p in enumerate(self.opts.positions): 362 if i>0: 363 result+=" , " 364 result+='"%s" %d' % (p,i) 365 result+=")\n" 366 for tm in usedTimes: 367 if abs(float(tm))>1e20: 368 continue 369 result+=self.setFile("%s_writeTime_%s_Time_%s.png" % (usedDirName,timelines.usedTime,tm)) 370 result+='set title "Directory: %s WriteTime: %s Time: %s"\n' % (self.opts.dirName.replace("_","\\\\_"),timelines.usedTime,tm) 371 result+= "plot " 372 first=True 373 for val in self.opts.fields: 374 if first: 375 first=False 376 else: 377 result+=", " 378 result+='"-" title "%s" ' % val.replace("_","\\\\_") 379 result+="\n" 380 for v,t,vals in data: 381 if t==tm: 382 for v in vals: 383 result+="%g\n" % v 384 result+="e\n" 385 elif self.opts.basicMode=='lines': 386 # print_(self.opts.positions) 387 oPlots=timelines.getDataLocation(value=self.opts.fields, 388 position=self.opts.positions, 389 vectorMode=self.opts.vectorMode) 390 391 plots=oPlots[:] 392 rPlots=None 393 394 if reference: 395 rPlots=reference.getDataLocation(value=self.opts.fields, 396 position=self.opts.positions, 397 vectorMode=self.opts.vectorMode) 398 for gp,pos,val,comp,tv in rPlots: 399 plots.append((gp, 400 pos, 401 self.opts.refprefix+" "+val, 402 comp, 403 tv)) 404 if self.opts.referenceTime and reference!=None: 405 minTime,maxTime=reference.timeRange() 406 else: 407 minTime,maxTime=timelines.timeRange() 408 if self.opts.minTime: 409 minTime=self.opts.minTime 410 if self.opts.maxTime: 411 maxTime=self.opts.maxTime 412 result+= "set xrange [%g:%g]\n" % (minTime,maxTime) 413 if self.opts.collectLines=="fields": 414 for val in self.opts.fields: 415 vname=val 416 if val in timelines.vectors: 417 vname+="_"+self.opts.vectorMode 418 result+=self.setFile("%s_writeTime_%s_Value_%s.png" % (usedDirName,timelines.usedTime,vname)) 419 result+='set title "Directory: %s WriteTime: %s Value: %s"\n' % (self.opts.dirName.replace("_","\\\\_"),timelines.usedTime,vname.replace("_","\\\\\\_")) 420 result+= "plot " 421 first=True 422 for f,v,p,i,tl in plots: 423 if v==val: 424 if first: 425 first=False 426 else: 427 result+=" , " 428 if type(i)==int: 429 result+= ' "%s" using 1:%d title "%s" with lines ' % (f,i+2,p.replace("_","\\\\_")) 430 else: 431 result+= ' "%s" using 1:%s title "%s" with lines ' % (f,i,p.replace("_","\\\\_")) 432 433 result+="\n" 434 elif self.opts.collectLines=="positions": 435 for pos in self.opts.positions: 436 result+=self.setFile("%s_writeTime_%s_Position_%s.png" % (usedDirName,timelines.usedTime,pos)) 437 result+='set title "Directory: %s WriteTime: %s Position: %s"\n' % (self.opts.dirName.replace("_","\\\\_"),timelines.usedTime,pos.replace("_","\\\\_")) 438 result+= "plot " 439 first=True 440 for f,v,p,i,tl in plots: 441 if p==pos: 442 if first: 443 first=False 444 else: 445 result+=" , " 446 if type(i)==int: 447 result+= ' "%s" using 1:%d title "%s" with lines ' % (f,i+2,v.replace("_","\\\\_")) 448 else: 449 result+= ' "%s" using 1:%s title "%s" with lines ' % (f,i,v.replace("_","\\\\_")) 450 result+="\n" 451 452 else: 453 self.error("Unimplemented collection of lines:",self.opts.collectLines) 454 else: 455 self.error("Not implemented basicMode",self.opts.basicMode) 456 457 if self.opts.csvFile or self.opts.excelFile or self.opts.pandasData or self.opts.numpyData: 458 if self.opts.basicMode!='lines': 459 self.error("CSV and Excel-files currently only supported for lines-mode") 460 spread=plots[0][-1]() 461 for line in plots[1:]: 462 if line[3]==0: 463 sp=line[-1]() 464 try: 465 spread+=sp 466 except WrongDataSize: 467 if self.opts.resample: 468 for n in sp.names()[1:]: 469 data=spread.resample(sp, 470 n, 471 extendData=self.opts.extendData) 472 try: 473 spread.append(n,data) 474 except ValueError: 475 spread.append(self.opts.refprefix+" "+n,data) 476 else: 477 self.warning("Try the --resample-option") 478 raise 479 480 if self.opts.csvFile: 481 spread.writeCSV(self.opts.csvFile) 482 if self.opts.excelFile: 483 spread.getData().to_excel(self.opts.excelFile) 484 if self.opts.pandasData: 485 self.setData({"series":spread.getSeries(), 486 "dataFrame":spread.getData()}) 487 if self.opts.numpyData: 488 self.setData({"data":spread.data.copy()}) 489 490 elif self.opts.compare or self.opts.metrics: 491 statData={} 492 if self.opts.compare: 493 statData["compare"]={} 494 if self.opts.metrics: 495 statData["metrics"]={} 496 for p in self.opts.positions: 497 if self.opts.compare: 498 statData["compare"][p]={} 499 if self.opts.metrics: 500 statData["metrics"][p]={} 501 502 if self.opts.basicMode!='lines': 503 self.error("Compare currently only supported for lines-mode") 504 505 if self.opts.compare: 506 if rPlots==None: 507 self.error("No reference data specified. Can't compare") 508 elif len(rPlots)!=len(oPlots): 509 self.error("Number of original data sets",len(oPlots), 510 "is not equal to the reference data sets", 511 len(rPlots)) 512 513 for i,p in enumerate(oPlots): 514 pth,val,loc,ind,tl=p 515 if self.opts.compare: 516 rpth,rval,rloc,rind,rtl=rPlots[i] 517 if val!=rval or loc!=rloc or ind!=rind: 518 self.error("Original data",p,"and reference",rPlots[i], 519 "do not match") 520 data=tl() 521 try: 522 dataIndex=1+ind 523 if self.opts.metrics: 524 if not self.opts.silent: 525 print_("Metrics for",val,"on",loc,"index",ind,"(Path:",pth,")") 526 result=data.metrics(data.names()[dataIndex], 527 minTime=self.opts.minTime, 528 maxTime=self.opts.maxTime) 529 statData["metrics"][loc][val]=result 530 if not self.opts.silent: 531 print_(" Min :",result["min"]) 532 print_(" Max :",result["max"]) 533 print_(" Average :",result["average"]) 534 print_(" Weighted average :",result["wAverage"]) 535 if not self.opts.compare: 536 print_("Data size:",data.size()) 537 print_(" Time Range :",result["tMin"],result["tMax"]) 538 if self.opts.compare: 539 if not self.opts.silent: 540 print_("Comparing",val,"on",loc,"index",ind,"(path:",pth,")",end="") 541 ref=rtl() 542 if self.opts.compareOnOriginal: 543 if not self.opts.silent: 544 print_("on original data points") 545 result=data.compare(ref, 546 data.names()[dataIndex], 547 minTime=self.opts.minTime, 548 maxTime=self.opts.maxTime) 549 else: 550 if not self.opts.silent: 551 print_("on reference data points") 552 result=ref.compare(data, 553 data.names()[dataIndex], 554 minTime=self.opts.minTime, 555 maxTime=self.opts.maxTime) 556 557 statData["compare"][loc][val]=result 558 if not self.opts.silent: 559 print_(" Max difference :",result["max"]) 560 print_(" Average difference :",result["average"]) 561 print_(" Weighted average :",result["wAverage"]) 562 print_("Data size:",data.size(),"Reference:",ref.size()) 563 if not self.opts.metrics: 564 print_(" Time Range :",result["tMin"],result["tMax"]) 565 if not self.opts.silent: 566 print_() 567 except TypeError: 568 if self.opts.vectorMode=="mag": 569 self.error("Vector-mode 'mag' not supported for --compare and --metrics") 570 else: 571 raise 572 573 self.setData(statData) 574 else: 575 dest=sys.stdout 576 if self.opts.gnuplotFile: 577 dest=open(self.opts.gnuplotFile,"w") 578 579 dest.write(result)
580 581 # Should work with Python3 and Python2 582