Source code for PyFoam.LogAnalysis.FoamLogAnalyzer

#  ICE Revision: $Id$
"""Analyze OpenFOAM logs"""

from .TimeLineAnalyzer import TimeLineAnalyzer
from .CountLineAnalyzer import CountLineAnalyzer
from PyFoam.Basics.LineReader import LineReader
from PyFoam.Error import error

from PyFoam.ThirdParty.six import iteritems

from PyFoam.Basics.ProgressOutput import ProgressOutput
from PyFoam import configuration as config

from sys import stdout

from copy import deepcopy

import re

[docs]class FoamLogAnalyzer(object): """Base class for all analyzers Administrates and calls a number of LogLineAnlayzers for each line""" def __init__(self,progress=False): """ :param progress: Print time progress on console? """ self.analyzers={} self.time="" self.oDir="" self.line=LineReader(config().getboolean("SolverOutput","stripSpaces")) self.timeListeners=[] self.timeTriggers=[] self.resetFileTriggers=[] self.customExpr=re.compile("Custom([0-9]+)_(.+)") if progress: self.progressOut=ProgressOutput(stdout) else: self.progressOut=ProgressOutput() # tm=CountLineAnalyzer() tm=TimeLineAnalyzer() self.addAnalyzer("Time",tm) tm.addListener(self.setTime) self.analyzedLines = 0
[docs] def tearDown(self): """Remove reference to self in children (hoping to remove circular dependencies)""" for a in list(self.analyzers.values()): a.tearDown() a.setParent(None)
[docs] def collectData(self,structured=False): """Collect dictionaries of collected data (current state) from the analyzers :return: the dictionary""" result={} for nm in self.analyzers: data=self.analyzers[nm].getCurrentData(structured=structured) if len(data)>0: m=self.customExpr.match(nm) if m: if not "Custom" in result: result["Custom"]={} nr,name=m.groups() result["Custom"][name]=data # this will store custom data twice. But we'll keep it # for backward-compatibility result[nm]=data return result
[docs] def summarizeData(self,col=80): """Get a summary of the data""" result="="*col result+="\nt = {:20}\n".format(self.getTime()) data=self.collectData(structured=True) for k,v in iteritems(data): if k.find("Custom")==0 and len(k)>9 and k[8]=="_": kk=k[9:] elif k in ["Custom"]: continue else: kk=k if isinstance(v,(dict,)): result+=kk+" "+"-"*(col-len(kk)-1)+"\n" isDicts=True for k1,v1 in iteritems(v): if not isinstance(v1,(dict,)): isDicts=False break if not isDicts: maxLen=max([len(k1) for k1 in v.keys()]) wFormat="{:%d} : {:8g}" % maxLen chunks=[wFormat.format(k1,v[k1]) for k1 in sorted(v.keys())] maxLen=max([len(c) for c in chunks]) chunks=[c if len(c)>=maxLen else c+" "*(maxLen-len(c)) for c in chunks] nrChunks=col // (max([len(e) for e in chunks])+3) for i in range(0,len(chunks),nrChunks): result+=" | ".join(chunks[i:(i+nrChunks)])+"\n" else: maxLen=0 for k1,v1 in iteritems(v): maxLen=max(maxLen,max([len(k1)+2-8]+[len(k2) for k2 in v1.keys()])) wFormat="{:%d} : {:8g}" % maxLen chunks={} chunkLen=0 for k1,v1 in iteritems(v): chunks[k1]=[wFormat.format(k2,v1[k2]) for k2 in sorted(v1.keys())] chunkLen=max(chunkLen, max([len(e) for e in chunks[k1]])) for k1 in sorted(v.keys()): chunks[k1]=[k1+" "+"_"*(chunkLen-len(k1)-1)]+chunks[k1] chunks[k1]=[c if len(c)>=chunkLen else c+" "*(chunkLen-len(c)) for c in chunks[k1]] nrChunks=col // (chunkLen+3) for i in range(0,len(chunks[k1]),nrChunks): result+=" | ".join(chunks[k1][i:(i+nrChunks)])+"\n" else: result+=kk+": "+v+" "+"-"*(col-len(k)-len(v)-3)+"\n" return result;
[docs] def setTime(self,time): """Sets the time and alert all the LineAnalyzers that the time has changed :param time: the new value of the time """ if time!=self.time: self.progressOut.reset() self.time=time for listener in self.timeListeners: listener.timeChanged() for nm in self.analyzers: self.analyzers[nm].timeChanged() self.checkTriggers() data=self.collectData() for listener in self.timeListeners: try: # make sure everyone gets a separate copy listener.setDataSet(deepcopy(data)) except AttributeError: # seems that the listener doesn't want the data pass
[docs] def resetFile(self): """Propagate a reset to the actual analyzers""" for nm in self.analyzers: self.analyzers[nm].resetFile() for f in self.resetFileTriggers: f()
[docs] def writeProgress(self,msg): """Write a message to the progress output""" self.progressOut(msg)
[docs] def addTimeListener(self,listener): """:param listener: An object that is notified when the time changes. Has to implement a timeChanged method""" if not 'timeChanged' in dir(listener): error("Error. Object has no timeChanged-method:"+str(listener)) else: self.timeListeners.append(listener)
[docs] def addResetFileTrigger(self,f): self.resetFileTriggers.append(f)
[docs] def listAnalyzers(self): """:returns: A list with the names of the Analyzers""" return list(self.analyzers.keys())
[docs] def hasAnalyzer(self,name): """Is this LogLineAnalyzer name there""" return name in self.analyzers
[docs] def getAnalyzer(self,name): """Get the LogLineAnalyzer name""" if name in self.analyzers: return self.analyzers[name] else: return None
[docs] def addAnalyzer(self,name,obj): """Adds an analyzer obj - A LogLineAnalyzer name - the name of the analyzer""" obj.setParent(self) self.analyzers[name]=obj
[docs] def analyzeLine(self,line): """Calls all the anlyzers for a line""" self.analyzedLines+=1 for nm in self.analyzers: self.analyzers[nm].doAnalysis(line)
[docs] def analyze(self,fh): """Analyzes a file (one line at a time) fh - handle of the file""" while(self.line.read(fh)): self.analyzeLine(self.line.line)
[docs] def goOn(self): """Checks with all the analyzers If one analyzer returns False it returns False""" result=True for nm in self.analyzers: # print nm,self.analyzers[nm].goOn() result=result and self.analyzers[nm].goOn() return result
[docs] def getTime(self): """Gets the current time""" return str(self.time)
[docs] def isPastTime(self,check): """Are we past a given Time?""" if check is None: return False try: t=float(self.getTime()) return t>check except ValueError: return False
[docs] def setDirectory(self,d): """Sets the output directory for all the analyzers""" self.oDir=d for nm in self.analyzers: self.analyzers[nm].setDirectory(self.oDir)
[docs] def getDirectory(self): """Gets the output directory""" return self.oDir
[docs] def addTrigger(self,time,func,once=True,until=None): """Adds a trigger function that is to be called as soon as the simulation time exceeds a certain value :param time: the time at which the function should be triggered :param func: the trigger function :param once: Should this function be called once or at every time-step :param until: The time until which the trigger should be called""" data={} data["time"]=float(time) data["func"]=func if until!=None: data["until"]=float(until) once=False data["once"]=once self.timeTriggers.append(data)
[docs] def checkTriggers(self): """Check for and execute the triggered functions""" remove=[] for i in range(len(self.timeTriggers)): t=self.timeTriggers[i] if t["time"]<=self.time: t["func"]() if t["once"]: remove.append(i) elif "until" in t: if t["until"]<=self.time: remove.append(i) remove.reverse() for i in remove: self.timeTriggers.pop(i)
# Should work with Python3 and Python2