Example #1
0
 def testParameterFileWrite(self):
     par=ParameterFile(path.join(self.dest,"system","controlDict"),backup=True)
     self.assertEqual(par.readParameter("startTime"),"0")
     par.replaceParameter("startTime","42")
     self.assertEqual(par.readParameter("startTime"),"42")
     par.restore()
     self.assertEqual(par.readParameter("startTime"),"0")
Example #2
0
 def testParameterFileWrite(self):
     par = ParameterFile(path.join(self.dest, "system", "controlDict"),
                         backup=True)
     self.assertEqual(par.readParameter("startTime"), "0")
     par.replaceParameter("startTime", "42")
     self.assertEqual(par.readParameter("startTime"), "42")
     par.restore()
     self.assertEqual(par.readParameter("startTime"), "0")
Example #3
0
class BasicRunner(object):
    """Base class for the running of commands

    When the command is run the output is copied to a LogFile and
    (optionally) standard-out

    The argument list assumes for the first three elements the
    OpenFOAM-convention:

    <cmd> <dir> <case>

    The directory name for outputs is therefor created from <dir> and
    <case>

    Provides some handle-methods that are to be overloaded for
    additional functionality"""
    def __init__(self,
                 argv=None,
                 silent=False,
                 logname=None,
                 compressLog=False,
                 lam=None,
                 server=False,
                 restart=False,
                 noLog=False,
                 logTail=None,
                 remark=None,
                 jobId=None,
                 parameters=None,
                 writeState=True,
                 echoCommandLine=None):
        """:param argv: list with the tokens that are the command line
        if not set the standard command line is used
        :param silent: if True no output is sent to stdout
        :param logname: name of the logfile
        :param compressLog: Compress the logfile into a gzip
        :param lam: Information about a parallel run
        :param server: Whether or not to start the network-server
        :type lam: PyFoam.Execution.ParallelExecution.LAMMachine
        :param noLog: Don't output a log file
        :param logTail: only the last lines of the log should be written
        :param remark: User defined remark about the job
        :param parameters: User defined dictionary with parameters for
                 documentation purposes
        :param jobId: Job ID of the controlling system (Queueing system)
        :param writeState: Write the state to some files in the case
        :param echoCommandLine: Prefix that is printed with the command line. If unset nothing is printed
        """

        if sys.version_info < (2, 3):
            # Python 2.2 does not have the capabilities for the Server-Thread
            if server:
                warning(
                    "Can not start server-process because Python-Version is too old"
                )
            server = False

        if argv == None:
            self.argv = sys.argv[1:]
        else:
            self.argv = argv

        if oldApp():
            self.dir = path.join(self.argv[1], self.argv[2])
            if self.argv[2][-1] == path.sep:
                self.argv[2] = self.argv[2][:-1]
        else:
            self.dir = path.curdir
            if "-case" in self.argv:
                self.dir = self.argv[self.argv.index("-case") + 1]

        logname = calcLogname(logname, argv)

        try:
            sol = self.getSolutionDirectory()
        except OSError:
            e = sys.exc_info()[1]  # compatible with 2.x and 3.x
            error("Solution directory", self.dir,
                  "does not exist. No use running. Problem:", e)

        self.echoCommandLine = echoCommandLine
        self.silent = silent
        self.lam = lam
        self.origArgv = self.argv
        self.writeState = writeState
        self.__lastLastSeenWrite = 0
        self.__lastNowTimeWrite = 0

        if self.lam != None:
            self.argv = lam.buildMPIrun(self.argv)
            if config().getdebug("ParallelExecution"):
                debug("Command line:", " ".join(self.argv))
        self.cmd = " ".join(self.argv)
        foamLogger().info("Starting: " + self.cmd + " in " +
                          path.abspath(path.curdir))
        self.logFile = path.join(self.dir, logname + ".logfile")

        isRestart, restartnr, restartName, lastlog = findRestartFiles(
            self.logFile, sol)

        if restartName:
            self.logFile = restartName

        if not isRestart:
            from os import unlink
            from glob import glob
            for g in glob(self.logFile + ".restart*"):
                if path.isdir(g):
                    rmtree(g)
                else:
                    unlink(g)

        self.noLog = noLog
        self.logTail = logTail
        if self.logTail:
            if self.noLog:
                warning("Log tail", self.logTail,
                        "and no-log specified. Using logTail")
            self.noLog = True
            self.lastLines = []

        self.compressLog = compressLog
        if self.compressLog:
            self.logFile += ".gz"

        self.fatalError = False
        self.fatalFPE = False
        self.fatalStackdump = False
        self.endSeen = False
        self.warnings = 0
        self.started = False

        self.isRestarted = False
        if restart:
            self.controlDict = ParameterFile(path.join(self.dir, "system",
                                                       "controlDict"),
                                             backup=True)
            self.controlDict.replaceParameter("startFrom", "latestTime")
            self.isRestarted = True
        else:
            self.controlDict = None

        self.run = FoamThread(self.cmd, self)

        self.server = None
        if server:
            self.server = FoamServer(run=self.run, master=self)
            self.server.setDaemon(True)
            self.server.start()
            try:
                IP, PID, Port = self.server.info()
                f = open(path.join(self.dir, "PyFoamServer.info"), "w")
                print_(IP, PID, Port, file=f)
                f.close()
            except AttributeError:
                warning(
                    "There seems to be a problem with starting the server:",
                    self.server, "with attributes", dir(self.server))
                self.server = None

        self.createTime = None
        self.nowTime = None
        self.startTimestamp = time()

        self.stopMe = False
        self.writeRequested = False

        self.endTriggers = []

        self.lastLogLineSeen = None
        self.lastTimeStepSeen = None

        self.remark = remark
        self.jobId = jobId

        self.data = {"lines": 0}  #        self.data={"lines":0L}
        self.data["logfile"] = self.logFile
        self.data["casefullname"] = path.abspath(self.dir)
        self.data["casename"] = path.basename(path.abspath(self.dir))
        self.data["solver"] = path.basename(self.argv[0])
        self.data["solverFull"] = self.argv[0]
        self.data["commandLine"] = self.cmd
        self.data["hostname"] = uname()[1]
        if remark:
            self.data["remark"] = remark
        else:
            self.data["remark"] = "No remark given"
        if jobId:
            self.data["jobId"] = jobId
        parameterFile = sol.getParametersFromFile()
        if len(parameterFile):
            self.data["parameters"] = {}
            for k, v in parameterFile.items():
                self.data["parameters"][k] = makePrimitiveString(v)
        if parameters:
            if "parameters" not in self.data:
                self.data["parameters"] = {}
            self.data["parameters"].update(parameters)
        self.data["starttime"] = asctime()

    def appendTailLine(self, line):
        """Append lines to the tail of the log"""
        if len(self.lastLines) > 10 * self.logTail:
            # truncate the lines, but not too often
            self.lastLines = self.lastLines[-self.logTail:]
            self.writeTailLog()

        self.lastLines.append(line + "\n")

    def writeTailLog(self):
        """Write the last lines to the log"""
        fh = open(self.logFile, "w")
        if len(self.lastLines) <= self.logTail:
            fh.writelines(self.lastLines)
        else:
            fh.writelines(self.lastLines[-self.logTail:])
        fh.close()

    def start(self):
        """starts the command and stays with it till the end"""

        self.started = True
        if not self.noLog:
            if self.compressLog:
                fh = gzip.open(self.logFile, "w")
            else:
                fh = open(self.logFile, "w")

        self.startHandle()

        self.writeStartTime()
        self.writeTheState("Running")

        check = BasicRunnerCheck()

        if self.echoCommandLine:
            print_(self.echoCommandLine + " " + " ".join(self.argv))

        self.run.start()
        interrupted = False

        totalWarningLines = 0
        addLinesToWarning = 0
        collectWarnings = True

        while self.run.check():
            try:
                self.run.read()
                if not self.run.check():
                    break

                line = self.run.getLine()

                if "errorText" in self.data:
                    self.data["errorText"] += line + "\n"

                if addLinesToWarning > 0:
                    self.data["warningText"] += line + "\n"
                    addLinesToWarning -= 1
                    totalWarningLines += 1
                    if totalWarningLines > 500:
                        collectWarnings = False
                        addLinesToWarning = 0
                        self.data[
                            "warningText"] += "No more warnings added because limit of 500 lines exceeded"
                self.data["lines"] += 1
                self.lastLogLineSeen = time()
                self.writeLastSeen()

                tmp = check.getTime(line)
                if check.controlDictRead(line):
                    if self.writeRequested:
                        duration = config().getfloat("Execution",
                                                     "controlDictRestoreWait",
                                                     default=30.)
                        warning(
                            "Preparing to reset controlDict to old glory in",
                            duration, "seconds")
                        Timer(duration,
                              restoreControlDict,
                              args=[self.controlDict, self]).start()
                        self.writeRequested = False

                if tmp != None:
                    self.data["time"] = tmp
                    self.nowTime = tmp
                    self.writeTheState("Running", always=False)
                    self.writeNowTime()
                    self.lastTimeStepSeen = time()
                    if self.createTime == None:
                        # necessary because interFoam reports no creation time
                        self.createTime = tmp
                    try:
                        self.data["stepNr"] += 1
                    except KeyError:
                        self.data["stepNr"] = 1  # =1L

                    self.data["lasttimesteptime"] = asctime()

                tmp = check.getCreateTime(line)
                self.endSeen = check.endSeen
                if tmp != None:
                    self.createTime = tmp

                if not self.silent:
                    try:
                        print_(line)
                    except IOError:
                        e = sys.exc_info()[1]  # compatible with 2.x and 3.x
                        if e.errno != 32:
                            raise e
                        else:
                            # Pipe was broken
                            self.run.interrupt()

                if line.find("FOAM FATAL ERROR") >= 0 or line.find(
                        "FOAM FATAL IO ERROR") >= 0:
                    self.fatalError = True
                    self.data["errorText"] = "PyFoam found a Fatal Error "
                    if "time" in self.data:
                        self.data["errorText"] += "at time " + str(
                            self.data["time"]) + "\n"
                    else:
                        self.data["errorText"] += "before time started\n"
                    self.data["errorText"] += "\n" + line + "\n"

                if line.find("Foam::sigFpe::sigFpeHandler") >= 0:
                    self.fatalFPE = True
                if line.find("Foam::error::printStack") >= 0:
                    self.fatalStackdump = True

                if self.fatalError and line != "":
                    foamLogger().error(line)

                if line.find("FOAM Warning") >= 0:
                    self.warnings += 1
                    try:
                        self.data["warnings"] += 1
                    except KeyError:
                        self.data["warnings"] = 1
                    if collectWarnings:
                        addLinesToWarning = 20
                        if not "warningText" in self.data:
                            self.data["warningText"] = ""
                        else:
                            self.data["warningText"] += ("-" * 40) + "\n"
                        self.data[
                            "warningText"] += "Warning found by PyFoam on line "
                        self.data["warningText"] += str(
                            self.data["lines"]) + " "
                        if "time" in self.data:
                            self.data["warningText"] += "at time " + str(
                                self.data["time"]) + "\n"
                        else:
                            self.data["warningText"] += "before time started\n"
                        self.data["warningText"] += "\n" + line + "\n"

                if self.server != None:
                    self.server._insertLine(line)

                self.lineHandle(line)

                if not self.noLog:
                    fh.write(line + "\n")
                    fh.flush()
                elif self.logTail:
                    self.appendTailLine(line)

            except KeyboardInterrupt:
                e = sys.exc_info()[1]  # compatible with 2.x and 3.x
                foamLogger().warning("Keyboard Interrupt")
                self.run.interrupt()
                self.writeTheState("Interrupted")
                self.data["keyboardInterrupt"] = True
                interrupted = True

        if not "keyboardInterrupt" in self.data:
            self.data["keyboardInterrupt"] = self.run.keyboardInterupted

        self.data["interrupted"] = interrupted
        self.data["OK"] = self.runOK()
        self.data["endSeen"] = self.endSeen
        self.data["cpuTime"] = self.run.cpuTime()
        self.data["cpuUserTime"] = self.run.cpuUserTime()
        self.data["cpuSystemTime"] = self.run.cpuSystemTime()
        self.data["wallTime"] = self.run.wallTime()
        self.data["usedMemory"] = self.run.usedMemory()
        self.data["endtime"] = asctime()

        self.data["fatalError"] = self.fatalError
        self.data["fatalFPE"] = self.fatalFPE
        self.data["fatalStackdump"] = self.fatalStackdump

        self.writeNowTime(force=True)

        self.stopHandle()

        if not interrupted:
            if self.endSeen:
                self.writeTheState("Finished - Ended")
            else:
                self.writeTheState("Finished")

        for t in self.endTriggers:
            t()

        if not self.noLog:
            fh.close()
        elif self.logTail:
            self.writeTailLog()

        if self.server != None:
            self.server.deregister()
            self.server.kill()

        foamLogger().info("Finished")

        return self.data

    def writeToStateFile(self, fName, message):
        """Write a message to a state file"""
        if self.writeState:
            open(path.join(self.dir, "PyFoamState." + fName),
                 "w").write(message + "\n")

    def writeStartTime(self):
        """Write the real time the run was started at"""
        self.writeToStateFile("StartedAt", asctime())

    def writeTheState(self, state, always=True):
        """Write the current state the run is in"""
        if always or (time() - self.__lastLastSeenWrite) > 9:
            self.writeToStateFile("TheState", state)

    def writeLastSeen(self):
        if (time() - self.__lastLastSeenWrite) > 10:
            self.writeToStateFile("LastOutputSeen", asctime())
            self.__lastLastSeenWrite = time()

    def writeNowTime(self, force=False):
        if (time() - self.__lastNowTimeWrite) > 10 or force:
            self.writeToStateFile("CurrentTime", str(self.nowTime))
            self.__lastNowTimeWrite = time()

    def runOK(self):
        """checks whether the run was successful"""
        if self.started:
            return not self.fatalError and not self.fatalFPE and not self.fatalStackdump  # and self.run.getReturnCode()==0
        else:
            return False

    def startHandle(self):
        """to be called before the program is started"""
        pass

    def _writeStopAt(self, value, message):
        """Write stopAt to stop the run gracefully"""
        if not self.stopMe:
            self.stopMe = True
            if not self.isRestarted:
                if self.controlDict:
                    warning(
                        "The controlDict has already been modified. Restoring will be problementic"
                    )
                self.controlDict = ParameterFile(path.join(
                    self.dir, "system", "controlDict"),
                                                 backup=True)
            self.controlDict.replaceParameter("stopAt", value)
            warning(message)

    def stopGracefully(self):
        """Tells the runner to stop at the next convenient time"""
        self._writeStopAt("writeNow", "Stopping run and writting")

    def stopAtNextWrite(self):
        """Tells the runner to stop at the next write"""
        self._writeStopAt("nextWrite", "Stopping run at next write")

    def stopWithoutWrite(self):
        """Tells the runner to stop without writing"""
        self._writeStopAt("noWriteNow", "Stopping run without writing")

    def writeResults(self):
        """Writes the next possible time-step"""
        #        warning("writeResult is not yet implemented")
        if not self.writeRequested:
            if not self.isRestarted:
                if self.controlDict:
                    warning(
                        "The controlDict has already been modified. Restoring will be problementic"
                    )
                self.controlDict = ParameterFile(path.join(
                    self.dir, "system", "controlDict"),
                                                 backup=True)
            self.controlDict.replaceParameter("writeControl", "timeStep")
            self.controlDict.replaceParameter("writeInterval", "1")
            self.writeRequested = True

    def stopHandle(self):
        """called after the program has stopped"""
        if self.stopMe or self.isRestarted:
            self.controlDict.restore()

    def lineHandle(self, line):
        """called every time a new line is read"""
        pass

    def logName(self):
        """Get the name of the logfiles"""
        return self.logFile

    def getSolutionDirectory(self, archive=None):
        """:return: The directory of the case
        :rtype: PyFoam.RunDictionary.SolutionDirectory
        :param archive: Name of the directory for archiving results"""

        return SolutionDirectory(self.dir, archive=archive, parallel=True)

    def addEndTrigger(self, f):
        """:param f: A function that is to be executed at the end of the simulation"""
        self.endTriggers.append(f)
Example #4
0
class BasicRunner(object):
    """Base class for the running of commands

    When the command is run the output is copied to a LogFile and
    (optionally) standard-out

    The argument list assumes for the first three elements the
    OpenFOAM-convention:

    <cmd> <dir> <case>

    The directory name for outputs is therefor created from <dir> and
    <case>

    Provides some handle-methods that are to be overloaded for
    additional functionality"""

    def __init__(self,
                 argv=None,
                 silent=False,
                 logname=None,
                 compressLog=False,
                 lam=None,
                 server=False,
                 restart=False,
                 noLog=False,
                 logTail=None,
                 remark=None,
                 jobId=None,
                 parameters=None,
                 writeState=True,
                 echoCommandLine=None):
        """@param argv: list with the tokens that are the command line
        if not set the standard command line is used
        @param silent: if True no output is sent to stdout
        @param logname: name of the logfile
        @param compressLog: Compress the logfile into a gzip
        @param lam: Information about a parallel run
        @param server: Whether or not to start the network-server
        @type lam: PyFoam.Execution.ParallelExecution.LAMMachine
        @param noLog: Don't output a log file
        @param logTail: only the last lines of the log should be written
        @param remark: User defined remark about the job
        @param parameters: User defined dictionary with parameters for
                 documentation purposes
        @param jobId: Job ID of the controlling system (Queueing system)
        @param writeState: Write the state to some files in the case
        @param echoCommandLine: Prefix that is printed with the command line. If unset nothing is printed
        """

        if sys.version_info < (2,3):
            # Python 2.2 does not have the capabilities for the Server-Thread
            if server:
                warning("Can not start server-process because Python-Version is too old")
            server=False

        if argv==None:
            self.argv=sys.argv[1:]
        else:
            self.argv=argv

        if oldApp():
            self.dir=path.join(self.argv[1],self.argv[2])
            if self.argv[2][-1]==path.sep:
                self.argv[2]=self.argv[2][:-1]
        else:
            self.dir=path.curdir
            if "-case" in self.argv:
                self.dir=self.argv[self.argv.index("-case")+1]

        if logname==None:
            logname="PyFoam."+path.basename(argv[0])

        try:
            sol=self.getSolutionDirectory()
        except OSError:
            e = sys.exc_info()[1] # compatible with 2.x and 3.x
            error("Solution directory",self.dir,"does not exist. No use running. Problem:",e)

        self.echoCommandLine=echoCommandLine
        self.silent=silent
        self.lam=lam
        self.origArgv=self.argv
        self.writeState=writeState
        self.__lastLastSeenWrite=0
        self.__lastNowTimeWrite=0

        if self.lam!=None:
            self.argv=lam.buildMPIrun(self.argv)
            if config().getdebug("ParallelExecution"):
                debug("Command line:"," ".join(self.argv))
        self.cmd=" ".join(self.argv)
        foamLogger().info("Starting: "+self.cmd+" in "+path.abspath(path.curdir))
        self.logFile=path.join(self.dir,logname+".logfile")

        self.noLog=noLog
        self.logTail=logTail
        if self.logTail:
            if self.noLog:
                warning("Log tail",self.logTail,"and no-log specified. Using logTail")
            self.noLog=True
            self.lastLines=[]

        self.compressLog=compressLog
        if self.compressLog:
            self.logFile+=".gz"

        self.fatalError=False
        self.fatalFPE=False
        self.fatalStackdump=False

        self.warnings=0
        self.started=False

        self.isRestarted=False
        if restart:
            self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True)
            self.controlDict.replaceParameter("startFrom","latestTime")
            self.isRestarted=True
        else:
            self.controlDict=None

        self.run=FoamThread(self.cmd,self)

        self.server=None
        if server:
            self.server=FoamServer(run=self.run,master=self)
            self.server.setDaemon(True)
            self.server.start()
            try:
                IP,PID,Port=self.server.info()
                f=open(path.join(self.dir,"PyFoamServer.info"),"w")
                print_(IP,PID,Port,file=f)
                f.close()
            except AttributeError:
                warning("There seems to be a problem with starting the server:",self.server,"with attributes",dir(self.server))
                self.server=None

        self.createTime=None
        self.nowTime=None
        self.startTimestamp=time()

        self.stopMe=False
        self.writeRequested=False

        self.endTriggers=[]

        self.lastLogLineSeen=None
        self.lastTimeStepSeen=None

        self.remark=remark
        self.jobId=jobId

        self.data={"lines":0} #        self.data={"lines":0L}
        self.data["logfile"]=self.logFile
        self.data["casefullname"]=path.abspath(self.dir)
        self.data["casename"]=path.basename(path.abspath(self.dir))
        self.data["solver"]=path.basename(self.argv[0])
        self.data["solverFull"]=self.argv[0]
        self.data["commandLine"]=self.cmd
        self.data["hostname"]=uname()[1]
        if remark:
            self.data["remark"]=remark
        else:
            self.data["remark"]="No remark given"
        if jobId:
            self.data["jobId"]=jobId
        parameterFile=sol.getParametersFromFile()
        if len(parameterFile):
            self.data["parameters"]={}
            for k,v in parameterFile.items():
                self.data["parameters"][k]=makePrimitiveString(v)
        if parameters:
            if "parameters" not in self.data:
                self.data["parameters"]={}
            self.data["parameters"].update(parameters)
        self.data["starttime"]=asctime()

    def appendTailLine(self,line):
        """Append lines to the tail of the log"""
        if len(self.lastLines)>10*self.logTail:
            # truncate the lines, but not too often
            self.lastLines=self.lastLines[-self.logTail:]
            self.writeTailLog()

        self.lastLines.append(line+"\n")

    def writeTailLog(self):
        """Write the last lines to the log"""
        fh=open(self.logFile,"w")
        if len(self.lastLines)<=self.logTail:
            fh.writelines(self.lastLines)
        else:
            fh.writelines(self.lastLines[-self.logTail:])
        fh.close()

    def start(self):
        """starts the command and stays with it till the end"""

        self.started=True
        if not self.noLog:
            if self.compressLog:
                fh=gzip.open(self.logFile,"w")
            else:
                fh=open(self.logFile,"w")

        self.startHandle()

        self.writeStartTime()
        self.writeTheState("Running")

        check=BasicRunnerCheck()

        if self.echoCommandLine:
            print_(self.echoCommandLine+" "+" ".join(self.argv))

        self.run.start()
        interrupted=False

        totalWarningLines=0
        addLinesToWarning=0
        collectWarnings=True

        while self.run.check():
            try:
                self.run.read()
                if not self.run.check():
                    break

                line=self.run.getLine()

                if "errorText" in self.data:
                    self.data["errorText"]+=line+"\n"

                if addLinesToWarning>0:
                    self.data["warningText"]+=line+"\n"
                    addLinesToWarning-=1
                    totalWarningLines+=1
                    if totalWarningLines>500:
                        collectWarnings=False
                        addLinesToWarning=0
                        self.data["warningText"]+="No more warnings added because limit of 500 lines exceeded"
                self.data["lines"]+=1
                self.lastLogLineSeen=time()
                self.writeLastSeen()

                tmp=check.getTime(line)
                if check.controlDictRead(line):
                    if self.writeRequested:
                        duration=config().getfloat("Execution","controlDictRestoreWait",default=30.)
                        warning("Preparing to reset controlDict to old glory in",duration,"seconds")
                        Timer(duration,
                              restoreControlDict,
                              args=[self.controlDict,self]).start()
                        self.writeRequested=False

                if tmp!=None:
                    self.data["time"]=tmp
                    self.nowTime=tmp
                    self.writeTheState("Running",always=False)
                    self.writeNowTime()
                    self.lastTimeStepSeen=time()
                    if self.createTime==None:
                        # necessary because interFoam reports no creation time
                        self.createTime=tmp
                    try:
                        self.data["stepNr"]+=1
                    except KeyError:
                        self.data["stepNr"]=1  # =1L

                    self.data["lasttimesteptime"]=asctime()

                tmp=check.getCreateTime(line)
                if tmp!=None:
                    self.createTime=tmp

                if not self.silent:
                    try:
                        print_(line)
                    except IOError:
                        e = sys.exc_info()[1] # compatible with 2.x and 3.x
                        if e.errno!=32:
                            raise e
                        else:
                            # Pipe was broken
                            self.run.interrupt()

                if line.find("FOAM FATAL ERROR")>=0 or line.find("FOAM FATAL IO ERROR")>=0:
                    self.fatalError=True
                    self.data["errorText"]="PyFoam found a Fatal Error "
                    if "time" in self.data:
                        self.data["errorText"]+="at time "+str(self.data["time"])+"\n"
                    else:
                        self.data["errorText"]+="before time started\n"
                    self.data["errorText"]+="\n"+line+"\n"

                if line.find("Foam::sigFpe::sigFpeHandler")>=0:
                    self.fatalFPE=True
                if line.find("Foam::error::printStack")>=0:
                    self.fatalStackdump=True

                if self.fatalError and line!="":
                    foamLogger().error(line)

                if line.find("FOAM Warning")>=0:
                    self.warnings+=1
                    try:
                        self.data["warnings"]+=1
                    except KeyError:
                        self.data["warnings"]=1
                    if collectWarnings:
                        addLinesToWarning=20
                        if not "warningText" in self.data:
                            self.data["warningText"]=""
                        else:
                            self.data["warningText"]+=("-"*40)+"\n"
                        self.data["warningText"]+="Warning found by PyFoam on line "
                        self.data["warningText"]+=str(self.data["lines"])+" "
                        if "time" in self.data:
                            self.data["warningText"]+="at time "+str(self.data["time"])+"\n"
                        else:
                            self.data["warningText"]+="before time started\n"
                        self.data["warningText"]+="\n"+line+"\n"

                if self.server!=None:
                    self.server._insertLine(line)

                self.lineHandle(line)

                if not self.noLog:
                    fh.write(line+"\n")
                    fh.flush()
                elif self.logTail:
                    self.appendTailLine(line)

            except KeyboardInterrupt:
                e = sys.exc_info()[1] # compatible with 2.x and 3.x
                foamLogger().warning("Keyboard Interrupt")
                self.run.interrupt()
                self.writeTheState("Interrupted")
                interrupted=True

        self.data["interrupted"]=interrupted
        self.data["OK"]=self.runOK()
        self.data["cpuTime"]=self.run.cpuTime()
        self.data["cpuUserTime"]=self.run.cpuUserTime()
        self.data["cpuSystemTime"]=self.run.cpuSystemTime()
        self.data["wallTime"]=self.run.wallTime()
        self.data["usedMemory"]=self.run.usedMemory()
        self.data["endtime"]=asctime()

        self.data["fatalError"]=self.fatalError
        self.data["fatalFPE"]=self.fatalFPE
        self.data["fatalStackdump"]=self.fatalStackdump

        self.writeNowTime(force=True)

        self.stopHandle()

        if not interrupted:
            self.writeTheState("Finished")

        for t in self.endTriggers:
            t()

        if not self.noLog:
            fh.close()
        elif self.logTail:
            self.writeTailLog()

        if self.server!=None:
            self.server.deregister()
            self.server.kill()

        foamLogger().info("Finished")

        return self.data

    def writeToStateFile(self,fName,message):
        """Write a message to a state file"""
        if self.writeState:
            open(path.join(self.dir,"PyFoamState."+fName),"w").write(message+"\n")

    def writeStartTime(self):
        """Write the real time the run was started at"""
        self.writeToStateFile("StartedAt",asctime())

    def writeTheState(self,state,always=True):
        """Write the current state the run is in"""
        if always or (time()-self.__lastLastSeenWrite)>9:
            self.writeToStateFile("TheState",state)

    def writeLastSeen(self):
        if (time()-self.__lastLastSeenWrite)>10:
            self.writeToStateFile("LastOutputSeen",asctime())
            self.__lastLastSeenWrite=time()

    def writeNowTime(self,force=False):
        if (time()-self.__lastNowTimeWrite)>10 or force:
            self.writeToStateFile("CurrentTime",str(self.nowTime))
            self.__lastNowTimeWrite=time()

    def runOK(self):
        """checks whether the run was successful"""
        if self.started:
            return not self.fatalError and not self.fatalFPE and not self.fatalStackdump # and self.run.getReturnCode()==0
        else:
            return False

    def startHandle(self):
        """to be called before the program is started"""
        pass

    def stopGracefully(self):
        """Tells the runner to stop at the next convenient time"""
        if not self.stopMe:
            self.stopMe=True
            if not self.isRestarted:
                if self.controlDict:
                    warning("The controlDict has already been modified. Restoring will be problementic")
                self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True)
            self.controlDict.replaceParameter("stopAt","writeNow")
            warning("Stopping run at next write")

    def writeResults(self):
        """Writes the next possible time-step"""
        #        warning("writeResult is not yet implemented")
        if not self.writeRequested:
            if not self.isRestarted:
                if self.controlDict:
                    warning("The controlDict has already been modified. Restoring will be problementic")
                self.controlDict=ParameterFile(path.join(self.dir,"system","controlDict"),backup=True)
            self.controlDict.replaceParameter("writeControl","timeStep")
            self.controlDict.replaceParameter("writeInterval","1")
            self.writeRequested=True

    def stopHandle(self):
        """called after the program has stopped"""
        if self.stopMe or self.isRestarted:
            self.controlDict.restore()

    def lineHandle(self,line):
        """called every time a new line is read"""
        pass

    def logName(self):
        """Get the name of the logfiles"""
        return self.logFile

    def getSolutionDirectory(self,archive=None):
        """@return: The directory of the case
        @rtype: PyFoam.RunDictionary.SolutionDirectory
        @param archive: Name of the directory for archiving results"""

        return SolutionDirectory(self.dir,archive=archive,parallel=True)

    def addEndTrigger(self,f):
        """@param f: A function that is to be executed at the end of the simulation"""
        self.endTriggers.append(f)