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,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("--reference-prefix", 155 action="store", 156 dest="refprefix", 157 default="Reference", 158 help="Prefix that gets added to the reference lines. Default: %default") 159 160 data.add_option("--info", 161 action="store_true", 162 dest="info", 163 default=False, 164 help="Print info about the sampled data and exit") 165 output.add_option("--resample", 166 action="store_true", 167 dest="resample", 168 default=False, 169 help="Resample the reference value to the current x-axis (for CSV-output)") 170 output.add_option("--extend-data", 171 action="store_true", 172 dest="extendData", 173 default=False, 174 help="Extend the data range if it differs (for CSV-files)") 175 output.add_option("--silent", 176 action="store_true", 177 dest="silent", 178 default=False, 179 help="Don't write to screen (with the silent and the compare-options)") 180 181 numerics=OptionGroup(self.parser, 182 "Quantify", 183 "Metrics of the data and numerical comparisons") 184 self.parser.add_option_group(numerics) 185 numerics.add_option("--compare", 186 action="store_true", 187 dest="compare", 188 default=None, 189 help="Compare all data sets that are also in the reference data") 190 numerics.add_option("--metrics", 191 action="store_true", 192 dest="metrics", 193 default=None, 194 help="Print the metrics of the data sets") 195 numerics.add_option("--use-reference-for-comparison", 196 action="store_false", 197 dest="compareOnOriginal", 198 default=True, 199 help="Use the reference-data as the basis for the numerical comparison. Otherwise the original data will be used")
200
201 - def setFile(self,fName):
202 if self.opts.namePrefix: 203 fName=self.opts.namePrefix+"_"+fName 204 if self.opts.pictureDest: 205 fName=path.join(self.opts.pictureDest,fName) 206 207 name=fName 208 if self.opts.cleanFilename: 209 name=cleanFilename(fName) 210 return 'set output "%s"\n' % name
211
212 - def run(self):
213 # remove trailing slashif present 214 if self.opts.dirName[-1]==path.sep: 215 self.opts.dirName=self.opts.dirName[:-1] 216 217 usedDirName=self.opts.dirName.replace("/","_") 218 219 timelines=TimelineDirectory(self.parser.getArgs()[0], 220 dirName=self.opts.dirName, 221 writeTime=self.opts.writeTime) 222 reference=None 223 if self.opts.reference and self.opts.referenceCase: 224 self.error("Options --reference-directory and --reference-case are mutual exclusive") 225 if self.opts.csvFile and (self.opts.compare or self.opts.metrics): 226 self.error("Options --csv-file and --compare/--metrics are mutual exclusive") 227 228 if self.opts.reference: 229 reference=TimelineDirectory(self.parser.getArgs()[0], 230 dirName=self.opts.reference, 231 writeTime=self.opts.writeTime) 232 elif self.opts.referenceCase: 233 reference=TimelineDirectory(self.opts.referenceCase, 234 dirName=self.opts.dirName, 235 writeTime=self.opts.writeTime) 236 237 if self.opts.info: 238 self.setData({'writeTimes' : timelines.writeTimes, 239 'usedTimes' : timelines.usedTime, 240 'fields' : timelines.values, 241 'positions' : timelines.positions(), 242 'timeRange' : timelines.timeRange()}) 243 244 if not self.opts.silent: 245 print_("Write Times : ",timelines.writeTimes) 246 print_("Used Time : ",timelines.usedTime) 247 print_("Fields : ",timelines.values,end="") 248 if len(timelines.vectors)>0: 249 if not self.opts.silent: 250 print_(" Vectors: ",timelines.vectors) 251 self.setData({'vectors':timelines.vectors}) 252 else: 253 if not self.opts.silent: 254 print_() 255 if not self.opts.silent: 256 print_("Positions : ",timelines.positions()) 257 print_("Time range : ",timelines.timeRange()) 258 259 if reference: 260 refData={'writeTimes' : reference.writeTimes, 261 'fields' : reference.values, 262 'positions' : reference.positions(), 263 'timeRange' : reference.timeRange()} 264 265 if not self.opts.silent: 266 print_("\nReference Data") 267 print_("Write Times : ",reference.writeTimes) 268 print_("Fields : ",reference.values,end="") 269 if len(reference.vectors)>0: 270 if not self.opts.silent: 271 print_(" Vectors: ",reference.vectors) 272 refData["vectors"]=reference.vectors 273 else: 274 if not self.opts.silent: 275 print_() 276 if not self.opts.silent: 277 print_("Positions : ",reference.positions()) 278 print_("Time range : ",reference.timeRange()) 279 self.setData({"reference":refData}) 280 281 return 0 282 283 if self.opts.fields==None: 284 self.opts.fields=timelines.values 285 else: 286 for v in self.opts.fields: 287 if v not in timelines.values: 288 self.error("The requested value",v,"not in possible values",timelines.values) 289 if self.opts.positions==None: 290 self.opts.positions=timelines.positions() 291 else: 292 pos=self.opts.positions 293 self.opts.positions=[] 294 for p in pos: 295 try: 296 p=int(p) 297 if p<0 or p>=len(timelines.positions()): 298 self.error("Time index",p,"out of range for positons",timelines.positions()) 299 else: 300 self.opts.positions.append(timelines.positions()[p]) 301 except ValueError: 302 if p not in timelines.positions(): 303 self.error("Position",p,"not in",timelines.positions()) 304 else: 305 self.opts.positions.append(p) 306 307 if len(self.opts.positions)==0: 308 self.error("No valid positions") 309 310 result="set term png nocrop enhanced \n" 311 312 if self.opts.basicMode==None: 313 self.error("No mode selected. Do so with '--basic-mode'") 314 elif self.opts.basicMode=='bars': 315 if self.opts.time==None: 316 self.error("No times specified for bar-plots") 317 self.opts.time.sort() 318 if self.opts.referenceTime and reference!=None: 319 minTime,maxTime=reference.timeRange() 320 else: 321 minTime,maxTime=timelines.timeRange() 322 usedTimes=[] 323 hasMin=False 324 for t in self.opts.time: 325 if t<minTime: 326 if not hasMin: 327 usedTimes.append(minTime) 328 hasMin=True 329 elif t>maxTime: 330 usedTimes.append(maxTime) 331 break 332 else: 333 usedTimes.append(t) 334 data=timelines.getData(usedTimes, 335 value=self.opts.fields, 336 position=self.opts.positions, 337 vectorMode=self.opts.vectorMode) 338 # print_(data) 339 result+="set style data histogram\n" 340 result+="set style histogram cluster gap 1\n" 341 result+="set style fill solid border -1\n" 342 result+="set boxwidth 0.9\n" 343 result+="set xtics border in scale 1,0.5 nomirror rotate by 90 offset character 0, 0, 0\n" 344 # set xtic rotate by -45\n" 345 result+="set xtics (" 346 for i,p in enumerate(self.opts.positions): 347 if i>0: 348 result+=" , " 349 result+='"%s" %d' % (p,i) 350 result+=")\n" 351 for tm in usedTimes: 352 if abs(float(tm))>1e20: 353 continue 354 result+=self.setFile("%s_writeTime_%s_Time_%s.png" % (usedDirName,timelines.usedTime,tm)) 355 result+='set title "Directory: %s WriteTime: %s Time: %s"\n' % (self.opts.dirName.replace("_","\\\\_"),timelines.usedTime,tm) 356 result+= "plot " 357 first=True 358 for val in self.opts.fields: 359 if first: 360 first=False 361 else: 362 result+=", " 363 result+='"-" title "%s" ' % val.replace("_","\\\\_") 364 result+="\n" 365 for v,t,vals in data: 366 if t==tm: 367 for v in vals: 368 result+="%g\n" % v 369 result+="e\n" 370 elif self.opts.basicMode=='lines': 371 # print_(self.opts.positions) 372 oPlots=timelines.getDataLocation(value=self.opts.fields, 373 position=self.opts.positions, 374 vectorMode=self.opts.vectorMode) 375 376 plots=oPlots[:] 377 rPlots=None 378 379 if reference: 380 rPlots=reference.getDataLocation(value=self.opts.fields, 381 position=self.opts.positions, 382 vectorMode=self.opts.vectorMode) 383 for gp,pos,val,comp,tv in rPlots: 384 plots.append((gp, 385 pos, 386 self.opts.refprefix+" "+val, 387 comp, 388 tv)) 389 if self.opts.referenceTime and reference!=None: 390 minTime,maxTime=reference.timeRange() 391 else: 392 minTime,maxTime=timelines.timeRange() 393 if self.opts.minTime: 394 minTime=self.opts.minTime 395 if self.opts.maxTime: 396 maxTime=self.opts.maxTime 397 result+= "set xrange [%g:%g]\n" % (minTime,maxTime) 398 if self.opts.collectLines=="fields": 399 for val in self.opts.fields: 400 vname=val 401 if val in timelines.vectors: 402 vname+="_"+self.opts.vectorMode 403 result+=self.setFile("%s_writeTime_%s_Value_%s.png" % (usedDirName,timelines.usedTime,vname)) 404 result+='set title "Directory: %s WriteTime: %s Value: %s"\n' % (self.opts.dirName.replace("_","\\\\_"),timelines.usedTime,vname.replace("_","\\\\\\_")) 405 result+= "plot " 406 first=True 407 for f,v,p,i,tl in plots: 408 if v==val: 409 if first: 410 first=False 411 else: 412 result+=" , " 413 if type(i)==int: 414 result+= ' "%s" using 1:%d title "%s" with lines ' % (f,i+2,p.replace("_","\\\\_")) 415 else: 416 result+= ' "%s" using 1:%s title "%s" with lines ' % (f,i,p.replace("_","\\\\_")) 417 418 result+="\n" 419 elif self.opts.collectLines=="positions": 420 for pos in self.opts.positions: 421 result+=self.setFile("%s_writeTime_%s_Position_%s.png" % (usedDirName,timelines.usedTime,pos)) 422 result+='set title "Directory: %s WriteTime: %s Position: %s"\n' % (self.opts.dirName.replace("_","\\\\_"),timelines.usedTime,pos.replace("_","\\\\_")) 423 result+= "plot " 424 first=True 425 for f,v,p,i,tl in plots: 426 if p==pos: 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,v.replace("_","\\\\_")) 433 else: 434 result+= ' "%s" using 1:%s title "%s" with lines ' % (f,i,v.replace("_","\\\\_")) 435 result+="\n" 436 437 else: 438 self.error("Unimplemented collection of lines:",self.opts.collectLines) 439 else: 440 self.error("Not implemented basicMode",self.opts.basicMode) 441 442 if self.opts.csvFile: 443 if self.opts.basicMode!='lines': 444 self.error("CSV-files currently only supported for lines-mode") 445 spread=plots[0][-1]() 446 for line in plots[1:]: 447 if line[3]==0: 448 sp=line[-1]() 449 try: 450 spread+=sp 451 except WrongDataSize: 452 if self.opts.resample: 453 for n in sp.names()[1:]: 454 data=spread.resample(sp, 455 n, 456 extendData=self.opts.extendData) 457 try: 458 spread.append(n,data) 459 except ValueError: 460 spread.append(self.opts.refprefix+" "+n,data) 461 else: 462 self.warning("Try the --resample-option") 463 raise 464 465 spread.writeCSV(self.opts.csvFile) 466 elif self.opts.compare or self.opts.metrics: 467 statData={} 468 if self.opts.compare: 469 statData["compare"]={} 470 if self.opts.metrics: 471 statData["metrics"]={} 472 for p in self.opts.positions: 473 if self.opts.compare: 474 statData["compare"][p]={} 475 if self.opts.metrics: 476 statData["metrics"][p]={} 477 478 if self.opts.basicMode!='lines': 479 self.error("Compare currently only supported for lines-mode") 480 481 if self.opts.compare: 482 if rPlots==None: 483 self.error("No reference data specified. Can't compare") 484 elif len(rPlots)!=len(oPlots): 485 self.error("Number of original data sets",len(oPlots), 486 "is not equal to the reference data sets", 487 len(rPlots)) 488 489 for i,p in enumerate(oPlots): 490 pth,val,loc,ind,tl=p 491 if self.opts.compare: 492 rpth,rval,rloc,rind,rtl=rPlots[i] 493 if val!=rval or loc!=rloc or ind!=rind: 494 self.error("Original data",p,"and reference",rPlots[i], 495 "do not match") 496 data=tl() 497 try: 498 dataIndex=1+ind 499 if self.opts.metrics: 500 if not self.opts.silent: 501 print_("Metrics for",val,"on",loc,"index",ind,"(Path:",pth,")") 502 result=data.metrics(data.names()[dataIndex], 503 minTime=self.opts.minTime, 504 maxTime=self.opts.maxTime) 505 statData["metrics"][loc][val]=result 506 if not self.opts.silent: 507 print_(" Min :",result["min"]) 508 print_(" Max :",result["max"]) 509 print_(" Average :",result["average"]) 510 print_(" Weighted average :",result["wAverage"]) 511 if not self.opts.compare: 512 print_("Data size:",data.size()) 513 print_(" Time Range :",result["tMin"],result["tMax"]) 514 if self.opts.compare: 515 if not self.opts.silent: 516 print_("Comparing",val,"on",loc,"index",ind,"(path:",pth,")",end="") 517 ref=rtl() 518 if self.opts.compareOnOriginal: 519 if not self.opts.silent: 520 print_("on original data points") 521 result=data.compare(ref, 522 data.names()[dataIndex], 523 minTime=self.opts.minTime, 524 maxTime=self.opts.maxTime) 525 else: 526 if not self.opts.silent: 527 print_("on reference data points") 528 result=ref.compare(data, 529 data.names()[dataIndex], 530 minTime=self.opts.minTime, 531 maxTime=self.opts.maxTime) 532 533 statData["compare"][loc][val]=result 534 if not self.opts.silent: 535 print_(" Max difference :",result["max"]) 536 print_(" Average difference :",result["average"]) 537 print_(" Weighted average :",result["wAverage"]) 538 print_("Data size:",data.size(),"Reference:",ref.size()) 539 if not self.opts.metrics: 540 print_(" Time Range :",result["tMin"],result["tMax"]) 541 if not self.opts.silent: 542 print_() 543 except TypeError: 544 if self.opts.vectorMode=="mag": 545 self.error("Vector-mode 'mag' not supported for --compare and --metrics") 546 else: 547 raise 548 549 self.setData(statData) 550 else: 551 dest=sys.stdout 552 if self.opts.gnuplotFile: 553 dest=open(self.opts.gnuplotFile,"w") 554 555 dest.write(result)
556 557 # Should work with Python3 and Python2 558