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