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 remark=None,
59 jobId=None,
60 writeState=True):
61 """@param argv: list with the tokens that are the command line
62 if not set the standard command line is used
63 @param silent: if True no output is sent to stdout
64 @param logname: name of the logfile
65 @param compressLog: Compress the logfile into a gzip
66 @param lam: Information about a parallel run
67 @param server: Whether or not to start the network-server
68 @type lam: PyFoam.Execution.ParallelExecution.LAMMachine
69 @param noLog: Don't output a log file
70 @param remark: User defined remark about the job
71 @param jobId: Job ID of the controlling system (Queueing system)
72 @param writeState: Write the state to some files in the case"""
73
74 if sys.version_info < (2,3):
75
76 if server:
77 warning("Can not start server-process because Python-Version is too old")
78 server=False
79
80 if argv==None:
81 self.argv=sys.argv[1:]
82 else:
83 self.argv=argv
84
85 if oldApp():
86 self.dir=path.join(self.argv[1],self.argv[2])
87 if self.argv[2][-1]==path.sep:
88 self.argv[2]=self.argv[2][:-1]
89 else:
90 self.dir=path.curdir
91 if "-case" in self.argv:
92 self.dir=self.argv[self.argv.index("-case")+1]
93
94 if logname==None:
95 logname="PyFoam."+path.basename(argv[0])
96
97 try:
98 sol=self.getSolutionDirectory()
99 except OSError,e:
100 error("Solution directory",self.dir,"does not exist. No use running. Problem:",e)
101
102 self.silent=silent
103 self.lam=lam
104 self.origArgv=self.argv
105 self.writeState=writeState
106 self.__lastLastSeenWrite=0
107 self.__lastNowTimeWrite=0
108
109 if self.lam!=None:
110 self.argv=lam.buildMPIrun(self.argv)
111 if config().getdebug("ParallelExecution"):
112 debug("Command line:",string.join(self.argv))
113 self.cmd=string.join(self.argv," ")
114 foamLogger().info("Starting: "+self.cmd+" in "+path.abspath(path.curdir))
115 self.logFile=path.join(self.dir,logname+".logfile")
116 self.noLog=noLog
117 self.compressLog=compressLog
118 if self.compressLog:
119 self.logFile+=".gz"
120
121 self.fatalError=False
122 self.fatalFPE=False
123 self.fatalStackdump=False
124
125 self.warnings=0
126 self.started=False
127
128 self.isRestarted=False
129 if restart:
130 self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True)
131 self.controlDict.replaceParameter("startFrom","latestTime")
132 self.isRestarted=True
133 else:
134 self.controlDict=None
135
136 self.run=FoamThread(self.cmd,self)
137
138 self.server=None
139 if server:
140 self.server=FoamServer(run=self.run,master=self)
141 self.server.setDaemon(True)
142 self.server.start()
143 try:
144 IP,PID,Port=self.server.info()
145 f=open(path.join(self.dir,"PyFoamServer.info"),"w")
146 print >>f,IP,PID,Port
147 f.close()
148 except AttributeError:
149 warning("There seems to be a problem with starting the server:",self.server,"with attributes",dir(self.server))
150 self.server=None
151
152 self.createTime=None
153 self.nowTime=None
154 self.startTimestamp=time()
155
156 self.stopMe=False
157 self.writeRequested=False
158
159 self.endTriggers=[]
160
161 self.lastLogLineSeen=None
162 self.lastTimeStepSeen=None
163
164 self.remark=remark
165 self.jobId=jobId
166
168 """starts the command and stays with it till the end"""
169
170 self.started=True
171 if not self.noLog:
172 if self.compressLog:
173 fh=gzip.open(self.logFile,"w")
174 else:
175 fh=open(self.logFile,"w")
176
177 self.startHandle()
178
179 self.writeStartTime()
180 self.writeTheState("Running")
181
182 check=BasicRunnerCheck()
183
184 self.run.start()
185 interrupted=False
186
187 while self.run.check():
188 try:
189 self.run.read()
190 if not self.run.check():
191 break
192
193 line=self.run.getLine()
194 self.lastLogLineSeen=time()
195 self.writeLastSeen()
196
197 tmp=check.getTime(line)
198 if check.controlDictRead(line):
199 if self.writeRequested:
200 warning("Preparing to reset controlDict to old glory")
201 Timer(config().getfloat("Execution","controlDictRestoreWait",default=30.),
202 restoreControlDict,
203 args=[self.controlDict,self]).start()
204 self.writeRequested=False
205
206 if tmp!=None:
207 self.nowTime=tmp
208 self.writeTheState("Running",always=False)
209 self.writeNowTime()
210 self.lastTimeStepSeen=time()
211 if self.createTime==None:
212
213 self.createTime=tmp
214
215 tmp=check.getCreateTime(line)
216 if tmp!=None:
217 self.createTime=tmp
218
219 if not self.silent:
220 try:
221 print line
222 except IOError,e:
223 if e.errno!=32:
224 raise e
225 else:
226
227 self.run.interrupt()
228
229 if line.find("FOAM FATAL ERROR")>=0 or line.find("FOAM FATAL IO ERROR")>=0:
230 self.fatalError=True
231 if line.find("Foam::sigFpe::sigFpeHandler")>=0:
232 self.fatalFPE=True
233 if line.find("Foam::error::printStack")>=0:
234 self.fatalStackdump=True
235
236 if self.fatalError and line!="":
237 foamLogger().error(line)
238
239 if line.find("FOAM Warning")>=0:
240 self.warnings+=1
241
242 if self.server!=None:
243 self.server._insertLine(line)
244
245 self.lineHandle(line)
246
247 if not self.noLog:
248 fh.write(line+"\n")
249 fh.flush()
250
251 except KeyboardInterrupt,e:
252 foamLogger().warning("Keyboard Interrupt")
253 self.run.interrupt()
254 self.writeTheState("Interrupted")
255 interrupted=True
256
257 self.writeNowTime(force=True)
258
259 self.stopHandle()
260
261 if not interrupted:
262 self.writeTheState("Finished")
263
264 for t in self.endTriggers:
265 t()
266
267 if not self.noLog:
268 fh.close()
269
270 if self.server!=None:
271 self.server.deregister()
272 self.server.kill()
273
274 foamLogger().info("Finished")
275
277 """Write a message to a state file"""
278 if self.writeState:
279 open(path.join(self.dir,"PyFoamState."+fName),"w").write(message+"\n")
280
282 """Write the real time the run was started at"""
283 self.writeToStateFile("StartedAt",asctime())
284
286 """Write the current state the run is in"""
287 if always or (time()-self.__lastLastSeenWrite)>9:
288 self.writeToStateFile("TheState",state)
289
291 if (time()-self.__lastLastSeenWrite)>10:
292 self.writeToStateFile("LastOutputSeen",asctime())
293 self.__lastLastSeenWrite=time()
294
296 if (time()-self.__lastNowTimeWrite)>10 or force:
297 self.writeToStateFile("CurrentTime",str(self.nowTime))
298 self.__lastNowTimeWrite=time()
299
301 """checks whether the run was successful"""
302 if self.started:
303 return not self.fatalError and not self.fatalFPE and not self.fatalStackdump
304 else:
305 return False
306
308 """to be called before the program is started"""
309 pass
310
312 """Tells the runner to stop at the next convenient time"""
313 if not self.stopMe:
314 self.stopMe=True
315 if not self.isRestarted:
316 if self.controlDict:
317 warning("The controlDict has already been modified. Restoring will be problementic")
318 self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True)
319 self.controlDict.replaceParameter("stopAt","writeNow")
320 warning("Stopping run at next write")
321
333
335 """called after the program has stopped"""
336 if self.stopMe or self.isRestarted:
337 self.controlDict.restore()
338
340 """called every time a new line is read"""
341 pass
342
344 """Get the name of the logfiles"""
345 return self.logFile
346
348 """@return: The directory of the case
349 @rtype: PyFoam.RunDictionary.SolutionDirectory
350 @param archive: Name of the directory for archiving results"""
351
352 return SolutionDirectory(self.dir,archive=archive,parallel=True)
353
355 """@param f: A function that is to be executed at the end of the simulation"""
356 self.endTriggers.append(f)
357
358 import re
359
361 """A small class that does primitve checking for BasicRunner
362 Duplicates other efforts, but ...."""
363
364 floatRegExp="[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?"
365
367 self.timeExpr=re.compile("^Time = (%f%)$".replace("%f%",self.floatRegExp))
368 self.createExpr=re.compile("^Create mesh for time = (%f%)$".replace("%f%",self.floatRegExp))
369
371 """Does this line contain time information?"""
372 m=self.timeExpr.match(line)
373 if m:
374 return float(m.group(1))
375 else:
376 return None
377
379 """Does this line contain mesh time information?"""
380 m=self.createExpr.match(line)
381 if m:
382 return float(m.group(1))
383 else:
384 return None
385
387 """Was the controlDict reread?"""
388 if line.find("Reading object controlDict from file")>=0:
389 return True
390 else:
391 return False
392