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

Source Code for Module PyFoam.Applications.SamplePlot

  1  #  ICE Revision: $Id: /local/openfoam/Python/PyFoam/PyFoam/Applications/SamplePlot.py 8488 2013-11-03T14:38:32.775063Z bgschaid  $ 
  2  """ 
  3  Application class that implements pyFoamSamplePlot.py 
  4  """ 
  5   
  6  import sys,string 
  7  from os import path 
  8  from optparse import OptionGroup 
  9   
 10  from .PyFoamApplication import PyFoamApplication 
 11  from PyFoam.RunDictionary.SampleDirectory import SampleDirectory 
 12  from PyFoam.Basics.SpreadsheetData import WrongDataSize 
 13   
 14  from PyFoam.Error import error,warning 
 15   
 16  from .PlotHelpers import cleanFilename 
 17   
 18  from PyFoam.ThirdParty.six import print_ 
 19   
20 -class SamplePlot(PyFoamApplication):
21 - def __init__(self, 22 args=None, 23 **kwargs):
24 description="""\ 25 Reads data from the sample-dictionary and generates appropriate 26 gnuplot-commands. As an option the data can be written to a CSV-file. 27 """ 28 29 PyFoamApplication.__init__(self, 30 args=args, 31 description=description, 32 usage="%prog [options] <casedir>", 33 nr=1, 34 changeVersion=False, 35 interspersed=True, 36 **kwargs)
37 38 modeChoices=["separate","timesInOne","fieldsInOne","linesInOne","complete"] 39
40 - def addOptions(self):
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("--line", 47 action="append", 48 default=None, 49 dest="line", 50 help="Thesample line from which 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("--pattern-for-line", 57 action="store", 58 default=None, 59 dest="linePattern", 60 help="Usually the name of the line is automatically determined from the file name by taking the first part. If this regular expression is specified then it is used: the first group in the pattern will be the line name") 61 data.add_option("--default-value-names", 62 action="store", 63 default=None, 64 dest="valueNames", 65 help="Usually the names of the values automatically determined from the file. If they are specified (as a comma separated list of names) then these names are used and all the files MUST have these values") 66 data.add_option("--no-extension-needed", 67 action="store_false", 68 default=True, 69 dest="needsExtension", 70 help="The files do not have an extension") 71 data.add_option("--is-distribution", 72 action="store_true", 73 default=False, 74 dest="isDistribution", 75 help="The files in the directory are distributions. This sets the names of the lines and fields accordingly") 76 data.add_option("--postfix-for-field-names", 77 action="append", 78 default=[], 79 dest="fieldPostfix", 80 help="Possible postfix for field names of the form 'name_postfix'. Note that this should not be a possible field name") 81 data.add_option("--prefix-for-field-names", 82 action="append", 83 default=[], 84 dest="fieldPrefix", 85 help="Possible prefix for field names of the form 'prefix_name'. Note that this should not be a possible field name") 86 data.add_option("--directory-name", 87 action="store", 88 default="samples", 89 dest="dirName", 90 help="Alternate name for the directory with the samples (Default: %default)") 91 data.add_option("--preferred-component", 92 action="store", 93 type="int", 94 default=None, 95 dest="component", 96 help="The component that should be used for vectors. Otherwise the absolute value is used") 97 data.add_option("--reference-directory", 98 action="store", 99 default=None, 100 dest="reference", 101 help="A reference directory. If fitting sample data is found there it is plotted alongside the regular data") 102 data.add_option("--reference-case", 103 action="store", 104 default=None, 105 dest="referenceCase", 106 help="A reference case where a directory with the same name is looked for. Mutual exclusive with --reference-directory") 107 108 scale=OptionGroup(self.parser, 109 "Scale", 110 "Scale the data before comparing (not used during plotting)") 111 self.parser.add_option_group(scale) 112 scale.add_option("--scale-data", 113 action="store", 114 type="float", 115 default=1, 116 dest="scaleData", 117 help="Scale the data by this factor. Default: %default") 118 scale.add_option("--offset-data", 119 action="store", 120 type="float", 121 default=0, 122 dest="offsetData", 123 help="Offset the data by this factor. Default: %default") 124 scale.add_option("--scale-x-axis", 125 action="store", 126 type="float", 127 default=1, 128 dest="scaleXAxis", 129 help="Scale the x-axis by this factor. Default: %default") 130 scale.add_option("--offset-x-axis", 131 action="store", 132 type="float", 133 default=0, 134 dest="offsetXAxis", 135 help="Offset the x-axis by this factor. Default: %default") 136 137 scale.add_option("--scale-reference-data", 138 action="store", 139 type="float", 140 default=1, 141 dest="scaleReferenceData", 142 help="Scale the reference data by this factor. Default: %default") 143 scale.add_option("--offset-reference-data", 144 action="store", 145 type="float", 146 default=0, 147 dest="offsetReferenceData", 148 help="Offset the reference data by this factor. Default: %default") 149 scale.add_option("--scale-reference-x-axis", 150 action="store", 151 type="float", 152 default=1, 153 dest="scaleReferenceXAxis", 154 help="Scale the reference x-axis by this factor. Default: %default") 155 scale.add_option("--offset-reference-x-axis", 156 action="store", 157 type="float", 158 default=0, 159 dest="offsetReferenceXAxis", 160 help="Offset the reference x-axis by this factor. Default: %default") 161 162 time=OptionGroup(self.parser, 163 "Time", 164 "Select the times to plot") 165 self.parser.add_option_group(time) 166 167 time.add_option("--time", 168 action="append", 169 default=None, 170 dest="time", 171 help="The times that are plotted (can be used more than once). If none are specified all found times are used") 172 time.add_option("--min-time", 173 action="store", 174 type="float", 175 default=None, 176 dest="minTime", 177 help="The smallest time that should be used") 178 time.add_option("--max-time", 179 action="store", 180 type="float", 181 default=None, 182 dest="maxTime", 183 help="The biggest time that should be used") 184 time.add_option("--fuzzy-time", 185 action="store_true", 186 default=False, 187 dest="fuzzyTime", 188 help="Try to find the next timestep if the time doesn't match exactly") 189 time.add_option("--latest-time", 190 action="store_true", 191 default=False, 192 dest="latestTime", 193 help="Take the latest time from the data") 194 time.add_option("--reference-time", 195 action="store", 196 default=None, 197 dest="referenceTime", 198 help="Take this time from the reference data (instead of using the same time as the regular data)") 199 time.add_option("--tolerant-reference-time", 200 action="store_true", 201 default=False, 202 dest="tolerantReferenceTime", 203 help="Take the reference-time that is nearest to the selected time") 204 205 output=OptionGroup(self.parser, 206 "Appearance", 207 "How it should be plotted") 208 self.parser.add_option_group(output) 209 210 output.add_option("--mode", 211 type="choice", 212 default="separate", 213 dest="mode", 214 action="store", 215 choices=self.modeChoices, 216 help="What kind of plots are generated: a) separate for every time, line and field b) all times of a field in one plot c) all fields of a time in one plot d) all lines in one plot e) everything in one plot (Names: "+", ".join(self.modeChoices)+") Default: %default") 217 output.add_option("--unscaled", 218 action="store_false", 219 dest="scaled", 220 default=True, 221 help="Don't scale a value to the same range for all plots") 222 output.add_option("--scale-all", 223 action="store_true", 224 dest="scaleAll", 225 default=False, 226 help="Use the same scale for all fields (else use one scale for each field)") 227 output.add_option("--scale-domain", 228 action="store_true", 229 dest="scaleDomain", 230 default=False, 231 help="Automatically scale the x-domain to the same length for all plots") 232 output.add_option("--domain-minimum", 233 action="store", 234 type="float", 235 dest="domainMin", 236 default=None, 237 help="Use this value as the minimum for the x-domain for all plots") 238 output.add_option("--domain-maximum", 239 action="store", 240 type="float", 241 dest="domainMax", 242 default=None, 243 help="Use this value as the maximum for the x-domain for all plots") 244 output.add_option("--gnuplot-file", 245 action="store", 246 dest="gnuplotFile", 247 default=None, 248 help="Write the necessary gnuplot commands to this file. Else they are written to the standard output") 249 output.add_option("--picture-destination", 250 action="store", 251 dest="pictureDest", 252 default=None, 253 help="Directory the pictures should be stored to") 254 output.add_option("--name-prefix", 255 action="store", 256 dest="namePrefix", 257 default=None, 258 help="Prefix to the picture-name") 259 output.add_option("--csv-file", 260 action="store", 261 dest="csvFile", 262 default=None, 263 help="Write the data to a CSV-file instead of the gnuplot-commands") 264 output.add_option("--excel-file", 265 action="store", 266 dest="excelFile", 267 default=None, 268 help="Write the data to a Excel-file instead of the gnuplot-commands") 269 output.add_option("--pandas-data", 270 action="store_true", 271 dest="pandasData", 272 default=False, 273 help="Pass the raw data in pandas-format") 274 output.add_option("--numpy-data", 275 action="store_true", 276 dest="numpyData", 277 default=False, 278 help="Pass the raw data in numpy-format") 279 280 data.add_option("--info", 281 action="store_true", 282 dest="info", 283 default=False, 284 help="Print info about the sampled data and exit") 285 output.add_option("--style", 286 action="store", 287 default="lines", 288 dest="style", 289 help="Gnuplot-style for the data (Default: %default)") 290 output.add_option("--clean-filename", 291 action="store_true", 292 dest="cleanFilename", 293 default=False, 294 help="Clean filenames so that they can be used in HTML or Latex-documents") 295 output.add_option("--index-instead-of-time", 296 action="store_true", 297 dest="indexInsteadOfTime", 298 default=False, 299 help="Use an index instead of the time in the filename (mainly needed if the files are used to make a movie with FFMPEG)") 300 output.add_option("--reference-prefix", 301 action="store", 302 dest="refprefix", 303 default="Reference", 304 help="Prefix that gets added to the reference lines. Default: %default") 305 output.add_option("--resample-reference", 306 action="store_true", 307 dest="resampleReference", 308 default=False, 309 help="Resample the reference value to the current x-axis (for CSV or Excel-output)") 310 output.add_option("--extend-data", 311 action="store_true", 312 dest="extendData", 313 default=False, 314 help="Extend the data range if it differs (for CSV or Excel-files)") 315 output.add_option("--silent", 316 action="store_true", 317 dest="silent", 318 default=False, 319 help="Don't write to screen (with the silent and the compare-options)") 320 321 numerics=OptionGroup(self.parser, 322 "Quantify", 323 "Metrics of the data and numerical comparisons") 324 self.parser.add_option_group(numerics) 325 numerics.add_option("--metrics", 326 action="store_true", 327 dest="metrics", 328 default=None, 329 help="Print the metrics of the data sets") 330 numerics.add_option("--compare", 331 action="store_true", 332 dest="compare", 333 default=None, 334 help="Compare all data sets that are also in the reference data") 335 numerics.add_option("--common-range-compare", 336 action="store_true", 337 dest="commonRange", 338 default=None, 339 help="When comparing two datasets only use the common time range") 340 numerics.add_option("--index-tolerant-compare", 341 action="store_true", 342 dest="indexTolerant", 343 default=None, 344 help="Compare two data sets even if they have different indizes") 345 numerics.add_option("--use-reference-for-comparison", 346 action="store_false", 347 dest="compareOnOriginal", 348 default=True, 349 help="Use the reference-data as the basis for the numerical comparison. Otherwise the original data will be used")
350
351 - def run(self):
352 if self.opts.isDistribution: 353 if self.opts.valueNames or self.opts.linePattern: 354 self.error("The option --is-distribution can not be used with --pattern-for-line or --default-value-names") 355 # self.opts.valueNames="normalized,raw" 356 self.opts.linePattern=".+istribution_(.+)" 357 self.opts.needsExtension=False 358 359 # remove trailing slashif present 360 if self.opts.dirName[-1]==path.sep: 361 self.opts.dirName=self.opts.dirName[:-1] 362 363 usedDirName=self.opts.dirName.replace("/","_") 364 365 if self.opts.valueNames==None: 366 usedValueNames=None 367 else: 368 usedValueNames=self.opts.valueNames.split(","), 369 370 samples=SampleDirectory(self.parser.getArgs()[0], 371 dirName=self.opts.dirName, 372 postfixes=self.opts.fieldPostfix, 373 prefixes=self.opts.fieldPrefix, 374 valueNames=usedValueNames, 375 namesFromFirstLine=self.opts.isDistribution, 376 linePattern=self.opts.linePattern, 377 needsExtension=self.opts.needsExtension) 378 reference=None 379 if self.opts.reference and self.opts.referenceCase: 380 self.error("Options --reference-directory and --reference-case are mutual exclusive") 381 if (self.opts.csvFile or self.opts.excelFile or self.opts.pandasData or self.opts.numpyData) and (self.opts.compare or self.opts.metrics): 382 self.error("Options --csv-file/--excel-file/--pandas-data/--numpy-data and --compare/--metrics are mutual exclusive") 383 384 if self.opts.reference: 385 reference=SampleDirectory(self.parser.getArgs()[0], 386 dirName=self.opts.reference, 387 postfixes=self.opts.fieldPostfix, 388 prefixes=self.opts.fieldPrefix) 389 elif self.opts.referenceCase: 390 reference=SampleDirectory(self.opts.referenceCase, 391 dirName=self.opts.dirName, 392 postfixes=self.opts.fieldPostfix, 393 prefixes=self.opts.fieldPrefix) 394 395 if reference: 396 if path.samefile(reference.dir,samples.dir): 397 self.error("Used sample directory",samples.dir, 398 "and reference directory",reference.dir, 399 "are the same") 400 401 lines=samples.lines() 402 times=samples.times 403 404 if self.opts.info: 405 if not self.opts.silent: 406 print_("Times : ",samples.times) 407 print_("Lines : ",samples.lines()) 408 print_("Fields: ",list(samples.values())) 409 410 self.setData({'times' : samples.times, 411 'lines' : samples.lines(), 412 'values' : list(samples.values())}) 413 414 if reference: 415 if not self.opts.silent: 416 print_("\nReference Data:") 417 print_("Times : ",reference.times) 418 print_("Lines : ",reference.lines()) 419 print_("Fields: ",list(reference.values())) 420 self.setData({'reference':{'times' : samples.times, 421 'lines' : samples.lines(), 422 'values' : list(samples.values())}}) 423 424 return 0 425 426 if self.opts.line==None: 427 # error("At least one line has to be specified. Found were",samples.lines()) 428 self.opts.line=lines 429 else: 430 for l in self.opts.line: 431 if l not in lines: 432 error("The line",l,"does not exist in",lines) 433 434 if self.opts.latestTime: 435 if self.opts.time: 436 self.opts.time.append(samples.times[-1]) 437 else: 438 self.opts.time=[samples.times[-1]] 439 440 if self.opts.maxTime or self.opts.minTime: 441 if self.opts.time: 442 error("Times",self.opts.time,"and range [",self.opts.minTime,",",self.opts.maxTime,"] set: contradiction") 443 self.opts.time=[] 444 if self.opts.maxTime==None: 445 self.opts.maxTime= 1e20 446 if self.opts.minTime==None: 447 self.opts.minTime=-1e20 448 449 for t in times: 450 if float(t)<=self.opts.maxTime and float(t)>=self.opts.minTime: 451 self.opts.time.append(t) 452 453 if len(self.opts.time)==0: 454 error("No times in range [",self.opts.minTime,",",self.opts.maxTime,"] found: ",times) 455 elif self.opts.time: 456 iTimes=self.opts.time 457 self.opts.time=[] 458 for t in iTimes: 459 if t in samples.times: 460 self.opts.time.append(t) 461 elif self.opts.fuzzyTime: 462 tf=float(t) 463 use=None 464 dist=1e20 465 for ts in samples.times: 466 if abs(tf-float(ts))<dist: 467 use=ts 468 dist=abs(tf-float(ts)) 469 if use and use not in self.opts.time: 470 self.opts.time.append(use) 471 else: 472 pass 473 # self.warning("Time",t,"not found in the sample-times. Use option --fuzzy") 474 if self.opts.tolerantReferenceTime: 475 if self.opts.referenceTime: 476 self.error("--tolerant-reference-time and --reference-time can't be used at the same time") 477 refTimes={} 478 for t in self.opts.time: 479 dist=1e20 480 for rt in reference.times: 481 if abs(float(t)-float(rt))<dist: 482 refTimes[t]=rt 483 dist=abs(float(t)-float(rt)) 484 485 plots=[] 486 oPlots=[] 487 rPlots=[] 488 489 if self.opts.mode=="separate": 490 if self.opts.time==None: 491 self.opts.time=samples.times 492 if self.opts.field==None: 493 self.opts.field=list(samples.values()) 494 if self.opts.line==None: 495 self.opts.line=samples.lines() 496 for t in self.opts.time: 497 for f in self.opts.field: 498 for l in self.opts.line: 499 plot=samples.getData(line=[l], 500 value=[f], 501 time=[t], 502 scale=(self.opts.scaleXAxis, 503 self.opts.scaleData), 504 offset=(self.opts.offsetXAxis, 505 self.opts.offsetData)) 506 oPlots.append(plot[:]) 507 if reference: 508 rT=[t] 509 if self.opts.referenceTime: 510 rT=[self.opts.referenceTime] 511 elif self.opts.tolerantReferenceTime: 512 rT=[refTimes[t]] 513 p=reference.getData(line=[l], 514 value=[f], 515 time=rT, 516 note=self.opts.refprefix+" ", 517 scale=(self.opts.scaleReferenceXAxis, 518 self.opts.scaleReferenceData), 519 offset=(self.opts.offsetReferenceXAxis, 520 self.opts.offsetReferenceData)) 521 rPlots.append(p) 522 plot+=p 523 plots.append(plot) 524 525 elif self.opts.mode=="timesInOne": 526 if self.opts.field==None: 527 self.opts.field=list(samples.values()) 528 if self.opts.line==None: 529 self.opts.line=samples.lines() 530 for f in self.opts.field: 531 for l in self.opts.line: 532 plot=samples.getData(line=[l], 533 value=[f], 534 time=self.opts.time) 535 oPlots.append(plot[:]) 536 537 if reference: 538 rT=self.opts.time 539 if self.opts.referenceTime: 540 rT=[self.opts.referenceTime] 541 elif self.opts.tolerantReferenceTime: 542 rT=[refTimes[t]] 543 p=reference.getData(line=[l], 544 value=[f], 545 time=rT, 546 note=self.opts.refprefix+" ") 547 rPlots.append(p) 548 plot+=p 549 550 plots.append(plot) 551 552 elif self.opts.mode=="fieldsInOne": 553 if self.opts.scaled and not self.opts.scaleAll: 554 warning("In mode '",self.opts.mode,"' all fields are scaled to the same value") 555 self.opts.scaleAll=True 556 557 if self.opts.time==None: 558 self.opts.time=samples.times 559 if self.opts.line==None: 560 self.opts.line=samples.lines() 561 for t in self.opts.time: 562 for l in self.opts.line: 563 plot=samples.getData(line=[l], 564 value=self.opts.field, 565 time=[t]) 566 oPlots.append(plot[:]) 567 if reference: 568 rT=t 569 if self.opts.referenceTime: 570 rT=self.opts.referenceTime 571 elif self.opts.tolerantReferenceTime: 572 rT=refTimes[t] 573 p=reference.getData(line=[l], 574 value=self.opts.field, 575 time=[rT], 576 note=self.opts.refprefix+" ") 577 rPlots.append(p) 578 plot+=p 579 580 plots.append(plot) 581 582 elif self.opts.mode=="linesInOne": 583 if self.opts.field==None: 584 self.opts.field=list(samples.values()) 585 if self.opts.time==None: 586 self.opts.time=samples.times 587 for f in self.opts.field: 588 for t in self.opts.time: 589 plot=samples.getData(line=self.opts.line, 590 value=[f], 591 time=[t]) 592 oPlots.append(plot[:]) 593 594 if reference: 595 rT=t 596 if self.opts.referenceTime: 597 rT=self.opts.referenceTime 598 elif self.opts.tolerantReferenceTime: 599 rT=refTimes[t] 600 p=reference.getData(line=self.opts.line, 601 value=[f], 602 time=[rT], 603 note=self.opts.refprefix+" ") 604 rPlots.append(p) 605 plot+=p 606 607 plots.append(plot) 608 609 elif self.opts.mode=="complete": 610 if self.opts.scaled and not self.opts.scaleAll: 611 warning("In mode '",self.opts.mode,"' all fields are scaled to the same value") 612 self.opts.scaleAll=True 613 614 plot=samples.getData(line=self.opts.line, 615 value=self.opts.field, 616 time=self.opts.time) 617 oPlots.append(plot[:]) 618 if reference: 619 rT=self.opts.time 620 if self.opts.referenceTime: 621 rT=[self.opts.referenceTime] 622 elif self.opts.tolerantReferenceTime: 623 rT=[refTimes[t]] 624 p=reference.getData(line=self.opts.line, 625 value=self.opts.field, 626 time=rT, 627 note=self.opts.refprefix+" ") 628 plot+=p 629 rPlots.append(p) 630 631 plots.append(plot) 632 633 xMin,xMax=None,None 634 if self.opts.scaleDomain: 635 if self.opts.domainMin or self.opts.domainMax: 636 self.error("--scale-domain used. Can't use --domain-minimum or --domain-maximum") 637 xMin,xMax=1e40,-1e40 638 for p in plots: 639 for d in p: 640 mi,mx=d.domain() 641 xMin=min(xMin,mi) 642 xMax=max(xMax,mx) 643 else: 644 xMin,xMax=self.opts.domainMin,self.opts.domainMax 645 646 if self.opts.scaled: 647 if self.opts.scaleAll: 648 vRange=None 649 else: 650 vRanges={} 651 652 for p in plots: 653 for d in p: 654 mi,ma=d.range(component=self.opts.component) 655 nm=d.name 656 if not self.opts.scaleAll: 657 if nm in vRanges: 658 vRange=vRanges[nm] 659 else: 660 vRange=None 661 662 if vRange==None: 663 vRange=mi,ma 664 else: 665 vRange=min(vRange[0],mi),max(vRange[1],ma) 666 if not self.opts.scaleAll: 667 vRanges[nm]=vRange 668 669 result="set term png\n" 670 671 plots=[p for p in plots if len(p)>0] 672 673 if len(plots)<1: 674 self.error("No plots produced. Nothing done") 675 676 for p in plots: 677 if len(p)<1: 678 continue 679 680 name="" 681 682 if self.opts.namePrefix: 683 name+=self.opts.namePrefix+"_" 684 name+=usedDirName 685 title=None 686 tIndex=times.index(p[0].time()) 687 688 # name+="_"+"_".join(self.opts.line) 689 690 if self.opts.mode=="separate": 691 name+="_%s" % (p[0].line()) 692 if self.opts.indexInsteadOfTime: 693 name+="_%s_%04d" % (p[0].name,tIndex) 694 else: 695 name+="_%s_t=%f" % (p[0].name,float(p[0].time())) 696 697 title="%s at t=%f on %s" % (p[0].name,float(p[0].time()),p[0].line()) 698 elif self.opts.mode=="timesInOne": 699 name+="_%s" % (p[0].line()) 700 if self.opts.time!=None: 701 name+="_"+"_".join(["t="+t for t in self.opts.time]) 702 name+="_%s" % p[0].name 703 title="%s on %s" % (p[0].name,p[0].line()) 704 elif self.opts.mode=="fieldsInOne": 705 name+="_%s" % (p[0].line()) 706 if self.opts.field!=None: 707 name+="_"+"_".join(self.opts.field) 708 if self.opts.time!=None: 709 name+="_"+"_".join(["t="+t for t in self.opts.time]) 710 name+="_%04d" % tIndex 711 title="t=%f on %s" % (float(p[0].time()),p[0].line()) 712 elif self.opts.mode=="linesInOne": 713 name+="_%s" % (p[0].name) 714 if self.opts.line!=None: 715 name+="_"+"_".join(self.opts.line) 716 if self.opts.indexInsteadOfTime: 717 name+="_%04d" % tIndex 718 else: 719 name+="_t=%f" % float(p[0].time()) 720 title="%s at t=%f" % (p[0].name,float(p[0].time())) 721 elif self.opts.mode=="complete": 722 pass 723 724 name+=".png" 725 if self.opts.pictureDest: 726 name=path.join(self.opts.pictureDest,name) 727 728 if self.opts.cleanFilename: 729 name=cleanFilename(name) 730 731 result+='set output "%s"\n' % name 732 if title!=None: 733 result+='set title "%s"\n' % title.replace("_","\\_") 734 735 result+="plot " 736 if self.opts.scaled: 737 if not self.opts.scaleAll: 738 vRange=vRanges[p[0].name] 739 740 # only scale if extremas are sufficiently different 741 if abs(vRange[0]-vRange[1])>1e-5*max(abs(vRange[0]),abs(vRange[1])) and max(abs(vRange[0]),abs(vRange[1]))>1e-10: 742 yRange="[%g:%g] " % vRange 743 else: 744 yRange="[]" 745 else: 746 yRange="[]" 747 748 if xMin or xMax: 749 xRange="[" 750 if xMin: 751 xRange+=str(xMin) 752 xRange+=":" 753 if xMax: 754 xRange+=str(xMax) 755 xRange+="]" 756 else: 757 xRange="[]" 758 759 if self.opts.scaled or xMin or xMax: 760 result+=xRange+yRange 761 762 first=True 763 764 for d in p: 765 if first: 766 first=False 767 else: 768 result+=", " 769 770 colSpec=d.index+1 771 if d.isVector(): 772 if self.opts.component!=None: 773 colSpec=d.index+1+self.opts.component 774 else: 775 colSpec="(sqrt($%d**2+$%d**2+$%d**2))" % (d.index+1,d.index+2,d.index+3) 776 777 # result+='"%s" using 1:%s ' % (d.file,colSpec) 778 779 def makeCol(spec,sc,off): 780 if type(spec)==str: 781 pre="" 782 else: 783 pre="$" 784 spec=str(spec) 785 if sc==1: 786 if off==0: 787 return spec 788 else: 789 return "(%s%s+%f)" % (pre,spec,off) 790 else: 791 if off==0: 792 return "(%s%s*%f)" % (pre,spec,sc) 793 else: 794 return "(%s%s*%f+%f)" % (pre,spec,sc,off)
795 796 result+='"%s" using %s:%s ' % (d.file, 797 makeCol(1,d.scale[0],d.offset[0]), 798 makeCol(colSpec,d.scale[1],d.offset[1])) 799 800 title=d.note 801 if self.opts.mode=="separate": 802 title+="" 803 elif self.opts.mode=="timesInOne": 804 title+="t=%f" % float(d.time()) 805 elif self.opts.mode=="fieldsInOne": 806 title+="%s" % d.name 807 elif self.opts.mode=="linesInOne": 808 title+="t=%f" % float(d.time()) 809 elif self.opts.mode=="complete": 810 title+="%s at t=%f" % (d.name,float(d.time())) 811 812 if len(self.opts.line)>1: 813 title+=" on %s" % d.line() 814 815 if title=="": 816 result+="notitle " 817 else: 818 result+='title "%s" ' % title.replace("_","\\_") 819 820 result+="with %s " % self.opts.style 821 822 result+="\n" 823 824 if self.opts.csvFile or self.opts.excelFile or self.opts.pandasData or self.opts.numpyData: 825 tmp=sum(plots,[]) 826 c=tmp[0]() 827 for p in tmp[1:]: 828 try: 829 c+=p() 830 except WrongDataSize: 831 if self.opts.resampleReference: 832 sp=p() 833 for n in sp.names()[1:]: 834 data=c.resample(sp, 835 n, 836 extendData=self.opts.extendData) 837 try: 838 c.append(n,data) 839 except ValueError: 840 c.append(self.opts.refprefix+" "+n,data) 841 else: 842 self.warning("Try the --resample-option") 843 raise 844 845 if self.opts.csvFile: 846 c.writeCSV(self.opts.csvFile) 847 if self.opts.excelFile: 848 c.getData().to_excel(self.opts.excelFile) 849 if self.opts.pandasData: 850 self.setData({"series":c.getSeries(), 851 "dataFrame":c.getData()}) 852 if self.opts.numpyData: 853 self.setData({"data":c.data.copy()}) 854 855 elif self.opts.compare or self.opts.metrics: 856 statData={} 857 if self.opts.compare: 858 statData["compare"]={} 859 if self.opts.metrics: 860 statData["metrics"]={} 861 for p in self.opts.line: 862 if self.opts.compare: 863 statData["compare"][p]={} 864 if self.opts.metrics: 865 statData["metrics"][p]={} 866 867 oPlots=[item for sublist in oPlots for item in sublist] 868 rPlots=[item for sublist in rPlots for item in sublist] 869 if len(rPlots)!=len(oPlots) and self.opts.compare: 870 self.error("Number of original data sets",len(oPlots), 871 "is not equal to the reference data sets", 872 len(rPlots)) 873 if len(rPlots)==0 and self.opts.metrics: 874 rPlots=[None]*len(oPlots) 875 876 for o,r in zip(oPlots,rPlots): 877 data=o(scaleData=self.opts.scaleData, 878 offsetData=self.opts.offsetData, 879 scaleX=self.opts.scaleXAxis, 880 offsetX=self.opts.offsetXAxis) 881 if self.opts.compare: 882 if o.name!=r.name or (o.index!=r.index and not self.opts.indexTolerant): 883 self.error("Data from original",o.name,o.index, 884 "and reference",r.name,r.index, 885 "do not match. Try --index-tolerant-compare if you're sure that the data is right") 886 ref=r(scaleData=self.opts.scaleReferenceData, 887 offsetData=self.opts.offsetReferenceData, 888 scaleX=self.opts.scaleReferenceXAxis, 889 offsetX=self.opts.offsetReferenceXAxis) 890 else: 891 ref=None 892 for i,n in enumerate(data.names()): 893 if i==0: 894 continue 895 indexName=o.name 896 if n.split(" ")[-1]!=indexName: 897 indexName=n.split(" ")[-1] 898 899 if self.opts.metrics: 900 if not self.opts.silent: 901 print_("Metrics for",indexName,"(Path:",o.file,")") 902 result=data.metrics(data.names()[i], 903 minTime=self.opts.minTime, 904 maxTime=self.opts.maxTime) 905 statData["metrics"][o.line()][indexName]=result 906 if not self.opts.silent: 907 print_(" Min :",result["min"]) 908 print_(" Max :",result["max"]) 909 print_(" Average :",result["average"]) 910 print_(" Weighted average :",result["wAverage"]) 911 if not self.opts.compare: 912 print_("Data size:",data.size()) 913 print_(" Time Range :",result["tMin"],result["tMax"]) 914 if self.opts.compare: 915 oname=data.names()[i] 916 if self.opts.referenceTime or self.opts.tolerantReferenceTime: 917 oname=ref.names()[i] 918 if not self.opts.silent: 919 print_("Comparing",indexName,"with name",oname,"(Path:",r.file,")",end="") 920 if self.opts.compareOnOriginal: 921 if not self.opts.silent: 922 print_("on original data points") 923 result=data.compare(ref, 924 data.names()[i], 925 otherName=oname,common=self.opts.commonRange, 926 minTime=self.opts.minTime, 927 maxTime=self.opts.maxTime) 928 else: 929 if not self.opts.silent: 930 print_("on reference data points") 931 result=ref.compare(data, 932 oname, 933 otherName=data.names()[i], 934 common=self.opts.commonRange, 935 minTime=self.opts.minTime, 936 maxTime=self.opts.maxTime) 937 statData["compare"][o.line()][indexName]=result 938 if not self.opts.silent: 939 print_(" Max difference :",result["max"],"(at",result["maxPos"],")") 940 print_(" Average difference :",result["average"]) 941 print_(" Weighted average :",result["wAverage"]) 942 print_("Data size:",data.size(),"Reference:",ref.size()) 943 if not self.opts.metrics: 944 print_(" Time Range :",result["tMin"],result["tMax"]) 945 if not self.opts.silent: 946 print_() 947 948 self.setData(statData) 949 else: 950 dest=sys.stdout 951 if self.opts.gnuplotFile: 952 dest=open(self.opts.gnuplotFile,"w") 953 954 dest.write(result)
955 956 # Should work with Python3 and Python2 957