1
2 """
3 Application class that implements pyFoamComparator
4 """
5
6 import sys
7 import re
8 import string
9 from xml.dom.minidom import parse
10 import xml.dom
11 from os import path
12
13 from PyFoam.Error import error
14 from PyFoam.Basics.Utilities import execute
15 from PyFoam.Execution.AnalyzedRunner import AnalyzedRunner
16 from PyFoam.Execution.ConvergenceRunner import ConvergenceRunner
17 from PyFoam.Execution.BasicRunner import BasicRunner
18 from PyFoam.Execution.UtilityRunner import UtilityRunner
19 from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile
20 from PyFoam.LogAnalysis.BoundingLogAnalyzer import BoundingLogAnalyzer
21 from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
22
23 from PyFoamApplication import PyFoamApplication
24 from PyFoam.FoamInformation import changeFoamVersion
25
28 description="""
29 Reads an XML-file that specifies a base case and a parameter-variation
30 and executes all the variations of that case
31 """
32
33 PyFoamApplication.__init__(self,description=description,usage="%prog [options] <xmlfile>",nr=1,interspersed=True)
34
36 self.parser.add_option("--test",action="store_true",default=False,dest="test",help="Only does the preparation steps but does not execute the actual solver an the end")
37 self.parser.add_option("--removeOld",action="store_true",default=False,dest="removeOld",help="Remove the directories from an old run without asking")
38 self.parser.add_option("--purge",action="store_true",default=False,dest="purge",help="Remove the case directories after evaluating")
39 self.parser.add_option("--no-purge",action="store_true",default=False,dest="nopurge",help="Don't remove the case directories after evaluating")
40 self.parser.add_option("--steady",action="store_true",default=False,dest="steady",help="Only runs the solver until convergence")
41 self.parser.add_option("--showDictionary",action="store_true",default=False,dest="showDict",help="Shows the parameter-dictionary after the running of the solver")
42 self.parser.add_option("--foamVersion",dest="foamVersion",default=None,help="Change the OpenFOAM-version that is to be used")
43
45 if self.opts.foamVersion!=None:
46 changeFoamVersion(self.opts.foamVersion)
47
48 fName=self.parser.getArgs()[0]
49
50 dom=parse(fName)
51 doc=dom.documentElement
52
53 if doc.tagName!='comparator':
54 error("Wrong root-element",doc.tagName,"Expected: 'comparator'")
55
56 self.data=ComparatorData(doc)
57
58 purge=False
59 if doc.hasAttribute('purge'):
60 purge=eval(doc.getAttribute('purge'))
61 if self.opts.purge:
62 purge=self.opts.purge
63 if self.opts.nopurge:
64 purge=False
65
66 steady=False
67 if doc.hasAttribute('steady'):
68 steady=eval(doc.getAttribute('steady'))
69 if self.opts.steady:
70 purge=self.opts.steady
71
72 print " Parameters read OK "
73 print
74
75 aLog=open(self.data.id+".overview","w")
76 rDir=self.data.id+".results"
77 execute("rm -rf "+rDir)
78 execute("mkdir "+rDir)
79
80 for i in range(len(self.data)):
81 runID=("%04d" % i)
82 print >>aLog,runID,
83
84 para=self.data[i]
85 para["template"]=self.data.template
86 para["extension"]=self.data.extension
87 para["id"]=self.data.id
88
89 print "Executing Variation",i+1,"of",len(self.data)
90 print "Parameters:",
91 for k,v in para.iteritems():
92 print "%s='%s' " % (k,v),
93 if v.find(" ")>=0 or v.find("\t")>=0:
94 v="'"+v+"'"
95 print >>aLog,v,
96
97 print
98
99 cName="%s.%04d" % (self.data.id, i)
100 log=open(cName+".log","w")
101
102 para["case"]=cName
103 print "Case-directory:",cName
104 para["results"]=path.join(rDir,runID)
105 print "Results directory:",para["results"]
106 execute("mkdir "+para["results"])
107
108 if path.exists(cName):
109 if self.opts.removeOld:
110 print " Removing old case-directory"
111 execute("rm -r "+cName)
112 else:
113 error("Case-directory",cName,"exists")
114
115 print " copying template"
116 out=execute("cp -r "+self.data.template+" "+cName)
117 print >>log,"---- Copying"
118 for l in out:
119 print >>log,l,
120
121 print " preparing"
122 ok,erg=self.data.prep.execute(para,log)
123 print >>aLog,ok,
124 for e in erg:
125 print >>aLog,e,
126 aLog.flush()
127
128 if self.opts.test:
129 print " Skipping execution"
130 else:
131 print " running the solver"
132 sys.stdout.flush()
133
134 if steady:
135 runnerClass=ConvergenceRunner
136 else:
137 runnerClass=AnalyzedRunner
138
139 run=runnerClass(BoundingLogAnalyzer(doTimelines=True,progress=True),
140 argv=[self.data.solver,".",cName],
141 silent=True,server=True)
142
143 run.start()
144 ok=run.runOK()
145 if ok:
146 print " executed OK"
147 else:
148 print " fatal error"
149
150 tEnd=-1
151
152 for aName in run.listAnalyzers():
153 a=run.getAnalyzer(aName)
154 if 'titles' in dir(a):
155 for tit in a.lines.getValueNames():
156 t,v=a.getTimeline(tit)
157 if len(v)>0:
158 para["result_"+aName+"_"+tit]=v[-1]
159 if t[-1]>tEnd:
160 tEnd=t[-1]
161
162 print >>aLog,run.runOK(),tEnd,run.run.wallTime(),
163
164 para["endTime"]=tEnd
165 para["runlog"]=run.logFile
166
167 if self.opts.showDict:
168 print para
169
170 print " evaluating results"
171
172 ok,erg=self.data.post.execute(para,log)
173
174 if ok:
175 print " Evaluation OK",
176 else:
177 print " Evaluation failed",
178
179 if len(erg)>0:
180 print ":",erg,
181 print
182
183 print >>aLog,ok,
184 for e in erg:
185 print >>aLog,e,
186
187 if purge:
188 print " removing the case-directory"
189 out=execute("rm -r "+cName)
190 print >>log,"---- Removing"
191 for l in out:
192 print >>log,l,
193
194 log.close()
195 print
196 print >>aLog
197 aLog.flush()
198
199 aLog.close()
200
202 """ The object that holds the actual data"""
203
205 """
206 @param doc: the parsed XML-data from which the object is constructed
207 """
208 self.name=doc.getAttribute("name")
209 if self.name=="":
210 error("No name for 'comparator' given")
211
212 base=doc.getElementsByTagName("base")
213 if base.length!=1:
214 error("One 'base'-element needed. Found",base.length)
215 self.__parseBase(base[0])
216
217 self.vList=[]
218 for v in doc.getElementsByTagName("variation"):
219 self.vList.append(Variation(v))
220
222 """@param e: The 'base'-element"""
223
224 self.template=e.getAttribute("template")
225 if self.template=="":
226 error("No template is given")
227 if not path.exists(self.template):
228 error("Template",self.template,"does not exist")
229 self.id=self.template
230 if e.hasAttribute('extension'):
231 self.extension=e.getAttribute('extension')
232 self.id+="."+self.extension
233 else:
234 self.extension=""
235 self.solver=e.getAttribute("solver")
236 if self.solver=="":
237 error("No solver is given")
238 prep=e.getElementsByTagName("preparation")
239 if prep.length!=1:
240 error("One 'preparation'-element needed. Found",prep.length)
241 self.prep=PreparationChain(prep[0])
242 post=e.getElementsByTagName("evaluation")
243 if post.length!=1:
244 error("One 'evaluation'-element needed. Found",post.length)
245 self.post=EvaluationChain(post[0])
246
248 """@return: The total number of variations"""
249 if len(self.vList)==0:
250 return 0
251 else:
252 nr=1l
253 for v in self.vList:
254 nr*=len(v)
255 return nr
256
258 """@param nr: Number of the variation
259 @return: dictionary with the variation"""
260 if nr>=len(self):
261 error("Index",nr,"of variation out of bounds: [0,",len(self)-1,"]")
262 result={}
263 tmp=nr
264 for v in self.vList:
265 k,val=v[tmp % len(v)]
266 result[k]=val
267 tmp/=len(v)
268 assert tmp==0
269
270 return result
271
273 """Abstract base class for a number of commands"""
275 """@param c: XML-Subtree that represents the chain"""
276 self.commands=[]
277 for e in c.childNodes:
278 if e.nodeType!=xml.dom.Node.ELEMENT_NODE:
279 continue
280 if not e.tagName in self.table.keys():
281 error("Tagname",e.tagName,"not in table of valid tags",self.table.keys())
282 self.commands.append(self.table[e.tagName](e))
283
285 """Executes the chain
286 @param para:A dictionary with the parameters
287 @param log: Logfile to write to"""
288
289 result=[]
290 status=True
291 for c in self.commands:
292 ok,erg=c.execute(para,log)
293 status=ok and status
294 if erg!=None:
295 if type(erg)==list:
296 result+=erg
297 else:
298 result.append(erg)
299
300 return status,result
301
303 """Chain of Preparation commands"""
314
316 """Chain of evaluation commands"""
326
328 """Abstract base class of all commands"""
330 pass
331
333 result=e.getAttribute(name)
334 if result=="":
335 error("Missing attribute",name,"in element",e.tagName)
336 return result
337
339 """@param vals: Dictionary with the keywords
340 @return: A boolean whether it completed successfully and a list with results (None if no results are generated)"""
341 error("Execute not implemented for",type(self))
342
344 """Replaces all strings enclosed by $$ with the parameters
345 @param orig: the original string
346 @param para: dictionary with the parameters"""
347
348 exp=re.compile("\$[^$]*\$")
349 tmp=orig
350
351 m=exp.search(tmp)
352 while m:
353 a,e=m.span()
354 pre=tmp[0:a]
355 post=tmp[e:]
356 mid=tmp[a+1:e-1]
357
358 if not mid in para.keys():
359 error("Key",mid,"not existing in keys",para.keys())
360
361 tmp=pre+para[mid]+post
362
363 m=exp.search(tmp)
364
365 return tmp
366
368 """Executes a shell command"""
372
374 cmd=self.replace(self.command,para)
375 print " Executing ",cmd,
376 sys.stdout.flush()
377 out=execute(cmd)
378
379 if len(out)>0:
380 print " -->",len(out),"lines output"
381 for l in out:
382 print >>log,"---- Command:",cmd
383 print >>log,l,
384 else:
385 print
386
387 return True,None
388
390 """Derives an additional value"""
395
397 tmp=self.replace(self.expression,para)
398 try:
399 val=eval(tmp)
400 except SyntaxError:
401 error("Syntax error in",tmp)
402 print " Setting",self.name,"to",val
403 para[self.name]=str(val)
404
405 return True,None
406
408 """Returns values from the chains dictionaries"""
412
414 if para.has_key(self.key):
415 return True,para[self.key]
416 else:
417 print "-----> ",self.key,"not in set of valid keys",para.keys()
418 print >>log,self.key,"not in set of valid keys of dictionary",para
419 return False,None
420
422 """Sets value in the chains dictionaries"""
427
429 para[self.key]=self.value
430 return True,None
431
433 """Executes a OpenFOAM-utility"""
438
451
453 """Executes a OpenFOAM-utility and extracts information"""
457
459 argv=[self.utility,".",para['case']]+self.options.split()
460 print " Executing and analyzing",string.join(argv),
461 sys.stdout.flush()
462 run=UtilityRunner(argv,silent=True,logname=string.join(argv,"_"))
463 run.add("data",self.regexp)
464 run.start()
465 data=run.analyzer.getData("data")
466 result=None
467 if data!=None:
468 result=[]
469 for a in data:
470 result.append(a)
471 if result==None:
472 print "no data",
473 else:
474 print result,
475
476 if run.runOK():
477 print
478 else:
479 print "---> there was a problem"
480
481 return run.runOK(),result
482
484 """Common class for commands that operate on dictionaries"""
488
490 f=self.replace(self.filename,para)
491 v=self.replace(self.value,para)
492 s=self.replace(self.subexpression,para)
493 k=self.replace(self.key,para)
494
495 try:
496 dictFile=ParsedParameterFile(f,backup=True)
497 val=dictFile[k]
498 except KeyError:
499 self.error("Key: ",k,"not existing in File",f)
500 except IOError,e:
501 self.error("Problem with file",k,":",e)
502
503 try:
504 exec "dictFile[k]"+s+"=v"
505 except Exception,e:
506 error("Problem with subexpression:",sys.exc_info()[0],":",e)
507
508 dictFile.writeFile()
509
510 return True,None
511
513 """Common class for commands that set values on fields"""
515 SetterCommand.__init__(self,c)
516 self.field=c.getAttribute("field")
517 self.filename=path.join("$case$","0",self.field)
518
520 """Sets an initial condition"""
525
529
531 """Sets a boundary condition"""
533 FieldSetterCommand.__init__(self,c)
534 self.patch=c.getAttribute("patch")
535 self.key="boundaryField"
536 self.subexpression="['"+self.patch+"']"
537 self.element=c.getAttribute("element")
538 if self.element=="":
539 self.element="value"
540 self.subexpression+="['"+self.element+"']"
541
545
547 """Writes a value to a dictionary"""
549 SetterCommand.__init__(self,c)
550 self.dir=c.getAttribute("directory")
551 self.dict=c.getAttribute("dictionary")
552 self.filename=path.join("$case$",self.dir,self.dict)
553 self.key=c.getAttribute("key")
554 self.subexpression=c.getAttribute("subexpression")
555 self.value=c.getAttribute("value")
556
560
562 """Copies the result of the last time-step to the resultsd directory"""
565
572
574 """Copies the log file to the results"""
577
579 print " Copy logfile"
580 execute("cp "+para["runlog"]+" "+para["results"])
581 return True,None
582
584 """Represenrs one variation"""
585
587 """@param e: the XML-data from which it is created"""
588
589 self.name=e.getAttribute("name")
590 if self.name=="":
591 error("No name for 'variation' given")
592 self.key=e.getAttribute("key")
593 if self.name=="":
594 error("No key for 'variation'",self.name," given")
595 self.values=[]
596 for v in e.getElementsByTagName("value"):
597 self.values.append(str(v.firstChild.data))
598
600 return "Variation "+self.name+" varies "+self.key+" over "+str(self.values)
601
603 """@return: number of values"""
604 return len(self.values)
605
607 return self.key,self.values[key]
608