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