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

Source Code for Module PyFoam.Applications.TimelinePlot

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