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

Source Code for Module PyFoam.Applications.Comparator

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