Package PyFoam :: Package Execution :: Module BasicRunner
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Execution.BasicRunner

  1  #  ICE Revision: $Id: /local/openfoam/Python/PyFoam/PyFoam/Execution/BasicRunner.py 7901 2012-03-08T16:14:41.526931Z bgschaid  $  
  2  """Run a OpenFOAM command""" 
  3   
  4  import sys 
  5  import string 
  6  import gzip 
  7  from os import path 
  8  from threading import Timer 
  9  from time import time,asctime 
 10   
 11  from PyFoam.FoamInformation import oldAppConvention as oldApp 
 12   
 13  if not 'curdir' in dir(path) or not 'sep' in dir(path): 
 14      print "Warning: Inserting symbols into os.path (Python-Version<2.3)" 
 15      path.curdir='.' 
 16      path.sep   ='/' 
 17       
 18  from FoamThread import FoamThread 
 19  from PyFoam.Infrastructure.FoamServer import FoamServer 
 20  from PyFoam.Infrastructure.Logging import foamLogger 
 21  from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory 
 22  from PyFoam.RunDictionary.ParameterFile import ParameterFile 
 23  from PyFoam.Error import warning,error,debug 
 24  from PyFoam import configuration as config 
 25   
26 -def restoreControlDict(ctrl,runner):
27 """Timed function to avoid time-stamp-problems""" 28 warning("Restoring the controlDict") 29 ctrl.restore() 30 runner.controlDict=None
31
32 -class BasicRunner(object):
33 """Base class for the running of commands 34 35 When the command is run the output is copied to a LogFile and 36 (optionally) standard-out 37 38 The argument list assumes for the first three elements the 39 OpenFOAM-convention: 40 41 <cmd> <dir> <case> 42 43 The directory name for outputs is therefor created from <dir> and 44 <case> 45 46 Provides some handle-methods that are to be overloaded for 47 additional functionality""" 48
49 - def __init__(self, 50 argv=None, 51 silent=False, 52 logname=None, 53 compressLog=False, 54 lam=None, 55 server=False, 56 restart=False, 57 noLog=False, 58 logTail=None, 59 remark=None, 60 jobId=None, 61 writeState=True):
62 """@param argv: list with the tokens that are the command line 63 if not set the standard command line is used 64 @param silent: if True no output is sent to stdout 65 @param logname: name of the logfile 66 @param compressLog: Compress the logfile into a gzip 67 @param lam: Information about a parallel run 68 @param server: Whether or not to start the network-server 69 @type lam: PyFoam.Execution.ParallelExecution.LAMMachine 70 @param noLog: Don't output a log file 71 @param logTail: only the last lines of the log should be written 72 @param remark: User defined remark about the job 73 @param jobId: Job ID of the controlling system (Queueing system) 74 @param writeState: Write the state to some files in the case""" 75 76 if sys.version_info < (2,3): 77 # Python 2.2 does not have the capabilities for the Server-Thread 78 if server: 79 warning("Can not start server-process because Python-Version is too old") 80 server=False 81 82 if argv==None: 83 self.argv=sys.argv[1:] 84 else: 85 self.argv=argv 86 87 if oldApp(): 88 self.dir=path.join(self.argv[1],self.argv[2]) 89 if self.argv[2][-1]==path.sep: 90 self.argv[2]=self.argv[2][:-1] 91 else: 92 self.dir=path.curdir 93 if "-case" in self.argv: 94 self.dir=self.argv[self.argv.index("-case")+1] 95 96 if logname==None: 97 logname="PyFoam."+path.basename(argv[0]) 98 99 try: 100 sol=self.getSolutionDirectory() 101 except OSError,e: 102 error("Solution directory",self.dir,"does not exist. No use running. Problem:",e) 103 104 self.silent=silent 105 self.lam=lam 106 self.origArgv=self.argv 107 self.writeState=writeState 108 self.__lastLastSeenWrite=0 109 self.__lastNowTimeWrite=0 110 111 if self.lam!=None: 112 self.argv=lam.buildMPIrun(self.argv) 113 if config().getdebug("ParallelExecution"): 114 debug("Command line:",string.join(self.argv)) 115 self.cmd=string.join(self.argv," ") 116 foamLogger().info("Starting: "+self.cmd+" in "+path.abspath(path.curdir)) 117 self.logFile=path.join(self.dir,logname+".logfile") 118 119 self.noLog=noLog 120 self.logTail=logTail 121 if self.logTail: 122 if self.noLog: 123 warning("Log tail",self.logTail,"and no-log specified. Using logTail") 124 self.noLog=True 125 self.lastLines=[] 126 127 self.compressLog=compressLog 128 if self.compressLog: 129 self.logFile+=".gz" 130 131 self.fatalError=False 132 self.fatalFPE=False 133 self.fatalStackdump=False 134 135 self.warnings=0 136 self.started=False 137 138 self.isRestarted=False 139 if restart: 140 self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True) 141 self.controlDict.replaceParameter("startFrom","latestTime") 142 self.isRestarted=True 143 else: 144 self.controlDict=None 145 146 self.run=FoamThread(self.cmd,self) 147 148 self.server=None 149 if server: 150 self.server=FoamServer(run=self.run,master=self) 151 self.server.setDaemon(True) 152 self.server.start() 153 try: 154 IP,PID,Port=self.server.info() 155 f=open(path.join(self.dir,"PyFoamServer.info"),"w") 156 print >>f,IP,PID,Port 157 f.close() 158 except AttributeError: 159 warning("There seems to be a problem with starting the server:",self.server,"with attributes",dir(self.server)) 160 self.server=None 161 162 self.createTime=None 163 self.nowTime=None 164 self.startTimestamp=time() 165 166 self.stopMe=False 167 self.writeRequested=False 168 169 self.endTriggers=[] 170 171 self.lastLogLineSeen=None 172 self.lastTimeStepSeen=None 173 174 self.remark=remark 175 self.jobId=jobId 176 177 self.data={"lines":0L} 178 self.data["logfile"]=self.logFile
179
180 - def appendTailLine(self,line):
181 """Append lines to the tail of the log""" 182 if len(self.lastLines)>10*self.logTail: 183 # truncate the lines, but not too often 184 self.lastLines=self.lastLines[-self.logTail:] 185 self.writeTailLog() 186 187 self.lastLines.append(line+"\n")
188
189 - def writeTailLog(self):
190 """Write the last lines to the log""" 191 fh=open(self.logFile,"w") 192 if len(self.lastLines)<=self.logTail: 193 fh.writelines(self.lastLines) 194 else: 195 fh.writelines(self.lastLines[-self.logTail:]) 196 fh.close()
197
198 - def start(self):
199 """starts the command and stays with it till the end""" 200 201 self.started=True 202 if not self.noLog: 203 if self.compressLog: 204 fh=gzip.open(self.logFile,"w") 205 else: 206 fh=open(self.logFile,"w") 207 208 self.startHandle() 209 210 self.writeStartTime() 211 self.writeTheState("Running") 212 213 check=BasicRunnerCheck() 214 215 self.run.start() 216 interrupted=False 217 218 while self.run.check(): 219 try: 220 self.run.read() 221 if not self.run.check(): 222 break 223 224 line=self.run.getLine() 225 self.data["lines"]+=1 226 self.lastLogLineSeen=time() 227 self.writeLastSeen() 228 229 tmp=check.getTime(line) 230 if check.controlDictRead(line): 231 if self.writeRequested: 232 duration=config().getfloat("Execution","controlDictRestoreWait",default=30.) 233 warning("Preparing to reset controlDict to old glory in",duration,"seconds") 234 Timer(duration, 235 restoreControlDict, 236 args=[self.controlDict,self]).start() 237 self.writeRequested=False 238 239 if tmp!=None: 240 self.data["time"]=tmp 241 self.nowTime=tmp 242 self.writeTheState("Running",always=False) 243 self.writeNowTime() 244 self.lastTimeStepSeen=time() 245 if self.createTime==None: 246 # necessary because interFoam reports no creation time 247 self.createTime=tmp 248 try: 249 self.data["stepNr"]+=1 250 except KeyError: 251 self.data["stepNr"]=1L 252 253 tmp=check.getCreateTime(line) 254 if tmp!=None: 255 self.createTime=tmp 256 257 if not self.silent: 258 try: 259 print line 260 except IOError,e: 261 if e.errno!=32: 262 raise e 263 else: 264 # Pipe was broken 265 self.run.interrupt() 266 267 if line.find("FOAM FATAL ERROR")>=0 or line.find("FOAM FATAL IO ERROR")>=0: 268 self.fatalError=True 269 if line.find("Foam::sigFpe::sigFpeHandler")>=0: 270 self.fatalFPE=True 271 if line.find("Foam::error::printStack")>=0: 272 self.fatalStackdump=True 273 274 if self.fatalError and line!="": 275 foamLogger().error(line) 276 277 if line.find("FOAM Warning")>=0: 278 self.warnings+=1 279 try: 280 self.data["warnings"]+=1 281 except KeyError: 282 self.data["warnings"]=1 283 284 if self.server!=None: 285 self.server._insertLine(line) 286 287 self.lineHandle(line) 288 289 if not self.noLog: 290 fh.write(line+"\n") 291 fh.flush() 292 elif self.logTail: 293 self.appendTailLine(line) 294 295 except KeyboardInterrupt,e: 296 foamLogger().warning("Keyboard Interrupt") 297 self.run.interrupt() 298 self.writeTheState("Interrupted") 299 interrupted=True 300 301 self.writeNowTime(force=True) 302 303 self.stopHandle() 304 305 if not interrupted: 306 self.writeTheState("Finished") 307 308 for t in self.endTriggers: 309 t() 310 311 if not self.noLog: 312 fh.close() 313 elif self.logTail: 314 self.writeTailLog() 315 316 if self.server!=None: 317 self.server.deregister() 318 self.server.kill() 319 320 foamLogger().info("Finished") 321 322 self.data["OK"]=self.runOK() 323 324 return self.data
325
326 - def writeToStateFile(self,fName,message):
327 """Write a message to a state file""" 328 if self.writeState: 329 open(path.join(self.dir,"PyFoamState."+fName),"w").write(message+"\n")
330
331 - def writeStartTime(self):
332 """Write the real time the run was started at""" 333 self.writeToStateFile("StartedAt",asctime())
334
335 - def writeTheState(self,state,always=True):
336 """Write the current state the run is in""" 337 if always or (time()-self.__lastLastSeenWrite)>9: 338 self.writeToStateFile("TheState",state)
339
340 - def writeLastSeen(self):
341 if (time()-self.__lastLastSeenWrite)>10: 342 self.writeToStateFile("LastOutputSeen",asctime()) 343 self.__lastLastSeenWrite=time()
344
345 - def writeNowTime(self,force=False):
346 if (time()-self.__lastNowTimeWrite)>10 or force: 347 self.writeToStateFile("CurrentTime",str(self.nowTime)) 348 self.__lastNowTimeWrite=time()
349
350 - def runOK(self):
351 """checks whether the run was successful""" 352 if self.started: 353 return not self.fatalError and not self.fatalFPE and not self.fatalStackdump # and self.run.getReturnCode()==0 354 else: 355 return False
356
357 - def startHandle(self):
358 """to be called before the program is started""" 359 pass
360
361 - def stopGracefully(self):
362 """Tells the runner to stop at the next convenient time""" 363 if not self.stopMe: 364 self.stopMe=True 365 if not self.isRestarted: 366 if self.controlDict: 367 warning("The controlDict has already been modified. Restoring will be problementic") 368 self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True) 369 self.controlDict.replaceParameter("stopAt","writeNow") 370 warning("Stopping run at next write")
371
372 - def writeResults(self):
373 """Writes the next possible time-step""" 374 # warning("writeResult is not yet implemented") 375 if not self.writeRequested: 376 if not self.isRestarted: 377 if self.controlDict: 378 warning("The controlDict has already been modified. Restoring will be problementic") 379 self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True) 380 self.controlDict.replaceParameter("writeControl","timeStep") 381 self.controlDict.replaceParameter("writeInterval","1") 382 self.writeRequested=True
383
384 - def stopHandle(self):
385 """called after the program has stopped""" 386 if self.stopMe or self.isRestarted: 387 self.controlDict.restore()
388
389 - def lineHandle(self,line):
390 """called every time a new line is read""" 391 pass
392
393 - def logName(self):
394 """Get the name of the logfiles""" 395 return self.logFile
396
397 - def getSolutionDirectory(self,archive=None):
398 """@return: The directory of the case 399 @rtype: PyFoam.RunDictionary.SolutionDirectory 400 @param archive: Name of the directory for archiving results""" 401 402 return SolutionDirectory(self.dir,archive=archive,parallel=True)
403
404 - def addEndTrigger(self,f):
405 """@param f: A function that is to be executed at the end of the simulation""" 406 self.endTriggers.append(f)
407 408 import re 409
410 -class BasicRunnerCheck(object):
411 """A small class that does primitve checking for BasicRunner 412 Duplicates other efforts, but ....""" 413 414 floatRegExp="[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?" 415
416 - def __init__(self):
417 # self.timeExpr=re.compile("^Time = (%f%)$".replace("%f%",self.floatRegExp)) 418 self.timeExpr=config().getRegexp("SolverOutput","timeregexp") 419 self.createExpr=re.compile("^Create mesh for time = (%f%)$".replace("%f%",self.floatRegExp))
420
421 - def getTime(self,line):
422 """Does this line contain time information?""" 423 m=self.timeExpr.match(line) 424 if m: 425 return float(m.group(2)) 426 else: 427 return None
428
429 - def getCreateTime(self,line):
430 """Does this line contain mesh time information?""" 431 m=self.createExpr.match(line) 432 if m: 433 return float(m.group(1)) 434 else: 435 return None
436
437 - def controlDictRead(self,line):
438 """Was the controlDict reread?""" 439 if line.find("Reading object controlDict from file")>=0: 440 return True 441 else: 442 return False
443