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
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
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
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
356 self.opts.linePattern=".+istribution_(.+)"
357 self.opts.needsExtension=False
358
359
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
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
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
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
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
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
957