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

Source Code for Module PyFoam.Applications.Comparator

  1  #  ICE Revision: $Id: Comparator.py 7746 2007-08-06 08:02:24Z bgschaid $  
  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   
26 -class Comparator(PyFoamApplication):
27 - def __init__(self):
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
35 - def addOptions(self):
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
44 - def run(self):
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
201 -class ComparatorData(object):
202 """ The object that holds the actual data""" 203
204 - def __init__(self,doc):
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
221 - def __parseBase(self,e):
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
247 - def __len__(self):
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
257 - def __getitem__(self,nr):
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
272 -class CommandChain(object):
273 """Abstract base class for a number of commands"""
274 - def __init__(self,c):
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
284 - def execute(self,para,log):
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
302 -class PreparationChain(CommandChain):
303 """Chain of Preparation commands"""
304 - def __init__(self,c):
305 self.table={"genericcommand":GenericCommand, 306 "derived":DerivedCommand, 307 "foamcommand":FoamCommand, 308 "foamutility":FoamUtilityCommand, 309 "initial":InitialCommand, 310 "dictwrite":DictWriteCommand, 311 "setdictionary":SetDictionaryCommand, 312 "boundary":BoundaryCommand} 313 CommandChain.__init__(self,c)
314
315 -class EvaluationChain(CommandChain):
316 """Chain of evaluation commands"""
317 - def __init__(self,c):
318 self.table={"genericcommand":GenericCommand, 319 "derived":DerivedCommand, 320 "foamutility":FoamUtilityCommand, 321 "foamcommand":FoamCommand, 322 "dictionary":DictionaryCommand, 323 "lastresult":LastResultCommand, 324 "copylog":CopyLogCommand} 325 CommandChain.__init__(self,c)
326
327 -class Command(object):
328 """Abstract base class of all commands"""
329 - def __init__(self,c):
330 pass
331
332 - def getNonEmpty(self,e,name):
333 result=e.getAttribute(name) 334 if result=="": 335 error("Missing attribute",name,"in element",e.tagName) 336 return result
337
338 - def execute(self,vals,log):
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
343 - def replace(self,orig,para):
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
367 -class GenericCommand(Command):
368 """Executes a shell command"""
369 - def __init__(self,c):
370 Command.__init__(self,c) 371 self.command=c.firstChild.data
372
373 - def execute(self,para,log):
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
389 -class DerivedCommand(Command):
390 """Derives an additional value"""
391 - def __init__(self,c):
392 Command.__init__(self,c) 393 self.name=self.getNonEmpty(c,"name") 394 self.expression=self.getNonEmpty(c,"expression")
395
396 - def execute(self,para,log):
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
407 -class DictionaryCommand(Command):
408 """Returns values from the chains dictionaries"""
409 - def __init__(self,c):
410 Command.__init__(self,c) 411 self.key=self.getNonEmpty(c,"key")
412
413 - def execute(self,para,log):
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
421 -class SetDictionaryCommand(Command):
422 """Sets value in the chains dictionaries"""
423 - def __init__(self,c):
424 Command.__init__(self,c) 425 self.key=self.getNonEmpty(c,"key") 426 self.value=self.getNonEmpty(c,"value")
427
428 - def execute(self,para,log):
429 para[self.key]=self.value 430 return True,None
431
432 -class FoamCommand(Command):
433 """Executes a OpenFOAM-utility"""
434 - def __init__(self,c):
435 Command.__init__(self,c) 436 self.utility=self.getNonEmpty(c,"utility") 437 self.options=c.getAttribute("options")
438
439 - def execute(self,para,log):
440 argv=[self.utility,".",para['case']]+self.options.split() 441 print " Executing",string.join(argv), 442 sys.stdout.flush() 443 run=BasicRunner(argv,silent=True,logname=string.join(argv,"_")) 444 run.start() 445 if run.runOK(): 446 print 447 else: 448 print "---> there was a problem" 449 450 return run.runOK(),None
451
452 -class FoamUtilityCommand(FoamCommand):
453 """Executes a OpenFOAM-utility and extracts information"""
454 - def __init__(self,c):
455 FoamCommand.__init__(self,c) 456 self.regexp=self.getNonEmpty(c,"regexp")
457
458 - def execute(self,para,log):
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
483 -class SetterCommand(Command):
484 """Common class for commands that operate on dictionaries"""
485 - def __init__(self,c):
486 Command.__init__(self,c) 487 self.value=c.getAttribute("value")
488
489 - def execute(self,para,log):
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
512 -class FieldSetterCommand(SetterCommand):
513 """Common class for commands that set values on fields"""
514 - def __init__(self,c):
515 SetterCommand.__init__(self,c) 516 self.field=c.getAttribute("field") 517 self.filename=path.join("$case$","0",self.field)
518
519 -class InitialCommand(FieldSetterCommand):
520 """Sets an initial condition"""
521 - def __init__(self,c):
522 FieldSetterCommand.__init__(self,c) 523 self.key="internalField" 524 self.subexpression=""
525
526 - def execute(self,para,log):
527 print " Setting initial condition for",self.field 528 return FieldSetterCommand.execute(self,para,log)
529
530 -class BoundaryCommand(FieldSetterCommand):
531 """Sets a boundary condition"""
532 - def __init__(self,c):
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
542 - def execute(self,para,log):
543 print " Setting",self.element,"on",self.patch,"for",self.field 544 return FieldSetterCommand.execute(self,para,log)
545
546 -class DictWriteCommand(SetterCommand):
547 """Writes a value to a dictionary"""
548 - def __init__(self,c):
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
557 - def execute(self,para,log):
558 print " Manipulating",self.key,"in",self.dict 559 return SetterCommand.execute(self,para,log)
560
561 -class LastResultCommand(Command):
562 """Copies the result of the last time-step to the resultsd directory"""
563 - def __init__(self,c):
564 Command.__init__(self,c)
565
566 - def execute(self,para,log):
567 print " Copy last result" 568 sol=SolutionDirectory(para["case"]) 569 sol.addToClone(sol.getLast()) 570 sol.cloneCase(path.join(para["results"],para["id"])) 571 return True,None
572
573 -class CopyLogCommand(Command):
574 """Copies the log file to the results"""
575 - def __init__(self,c):
576 Command.__init__(self,c)
577
578 - def execute(self,para,log):
579 print " Copy logfile" 580 execute("cp "+para["runlog"]+" "+para["results"]) 581 return True,None
582
583 -class Variation(object):
584 """Represenrs one variation""" 585
586 - def __init__(self,e):
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
599 - def __str__(self):
600 return "Variation "+self.name+" varies "+self.key+" over "+str(self.values)
601
602 - def __len__(self):
603 """@return: number of values""" 604 return len(self.values)
605
606 - def __getitem__(self,key):
607 return self.key,self.values[key]
608