1
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
27 """Timed function to avoid time-stamp-problems"""
28 warning("Restoring the controlDict")
29 ctrl.restore()
30 runner.controlDict=None
31
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
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
181 """Append lines to the tail of the log"""
182 if len(self.lastLines)>10*self.logTail:
183
184 self.lastLines=self.lastLines[-self.logTail:]
185 self.writeTailLog()
186
187 self.lastLines.append(line+"\n")
188
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
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
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
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
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
332 """Write the real time the run was started at"""
333 self.writeToStateFile("StartedAt",asctime())
334
336 """Write the current state the run is in"""
337 if always or (time()-self.__lastLastSeenWrite)>9:
338 self.writeToStateFile("TheState",state)
339
341 if (time()-self.__lastLastSeenWrite)>10:
342 self.writeToStateFile("LastOutputSeen",asctime())
343 self.__lastLastSeenWrite=time()
344
346 if (time()-self.__lastNowTimeWrite)>10 or force:
347 self.writeToStateFile("CurrentTime",str(self.nowTime))
348 self.__lastNowTimeWrite=time()
349
351 """checks whether the run was successful"""
352 if self.started:
353 return not self.fatalError and not self.fatalFPE and not self.fatalStackdump
354 else:
355 return False
356
358 """to be called before the program is started"""
359 pass
360
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
383
385 """called after the program has stopped"""
386 if self.stopMe or self.isRestarted:
387 self.controlDict.restore()
388
390 """called every time a new line is read"""
391 pass
392
394 """Get the name of the logfiles"""
395 return self.logFile
396
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
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
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
417
418 self.timeExpr=config().getRegexp("SolverOutput","timeregexp")
419 self.createExpr=re.compile("^Create mesh for time = (%f%)$".replace("%f%",self.floatRegExp))
420
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
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
438 """Was the controlDict reread?"""
439 if line.find("Reading object controlDict from file")>=0:
440 return True
441 else:
442 return False
443