Source code for PyFoam.RunDictionary.SolutionDirectory

#  ICE Revision: $Id$
"""Working with a solution directory"""

from PyFoam.Basics.Utilities import Utilities
from PyFoam.Basics.BasicFile import BasicFile
from PyFoam.Error import warning,error
from PyFoam import configuration as conf

from PyFoam.RunDictionary.TimeDirectory import TimeDirectory
from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile,WriteParameterFile

from PyFoam.Basics.DataStructures import DictProxy

from PyFoam.ThirdParty.six import print_

from os import listdir,path,mkdir,stat,environ
from platform import uname
from time import asctime
from stat import ST_CTIME
import tarfile,fnmatch,glob
import re,os

try:
    from os import getlogin
except ImportError:
    try:
        import PyFoam.ThirdParty.winhacks
    except ImportError:
        print_("Unable to import the getlogin function.")
        import sys
        sys.exit(-1)

[docs]class SolutionDirectory(Utilities): """Represents a solution directory In the solution directory subdirectories whose names are numbers are assumed to be solutions for a specific time-step""" def __init__(self, name, archive=None, paraviewLink=True, parallel=False, addLocalConfig=False, tolerant=False, region=None): """:param name: Name of the solution directory :param archive: name of the directory where the lastToArchive-method should copy files, if None no archive is created. Deprecated as it was never used :param paraviewLink: Create a symbolic link controlDict.foam for paraview :param tolerant: do not fail for minor inconsistencies :param parallel: use the first processor-subdirectory for the authorative information :param region: Mesh region for multi-region cases""" self.name=path.abspath(name) self.archive=None if archive!=None: self.archive=path.join(name,archive) if not path.exists(self.archive): mkdir(self.archive) self.region=region self.backups=[] self.parallel=parallel self.tolerant=tolerant self.lastReread=0 self.reread() self.dirPrefix='' if self.processorDirs() and parallel: self.dirPrefix = self.processorDirs()[0] self.essential=set([self.systemDir(), self.constantDir()]) # only add the initial directory if no template exists if not path.exists(path.join(self.name,"0.org")) and not self.initialDir() is None: self.addToClone(self.initialDir()) # PyFoam-specific self.addToClone("PyFoamHistory") self.addToClone("customRegexp") self.addToClone("LocalConfigPyFoam") # this usually comes with the tutorials self.addToClone("Allclean") self.addToClone("Allrun") self.addToClone("*.ipynb") emptyFoamFile=path.join(self.name,path.basename(self.name)+".foam") if paraviewLink and not path.exists(emptyFoamFile): dummy=open(emptyFoamFile,"w") # equivalent to touch if addLocalConfig: self.addLocalConfig() # These are used by PrepareCase self.addToClone("*.org") self.addToClone("*.template") self.addToClone("*.sh") self.addToClone("*.parameters") self.addToClone("derivedParameters.py") self.addToClone("*.postTemplate") self.addToClone("*.finalTemplate") self.__postprocDirs=[] self.__postprocInfo={} self.addPostprocDir(".") self.addPostprocDir("postProcessing",fail=False)
[docs] def regions(self): """Detect sub-region cases by looking through constant and finding directories with polyMesh-directories""" regions=[] for f in listdir(self.constantDir()): fName=path.join(self.constantDir(),f) if path.isdir(fName): pF=path.join(fName,"polyMesh") if path.exists(pF) and path.isdir(pF): if self.region is None: regions.append(f) else: # sub-regions. Do they even exist? regions.append(path.join(self.region,f)) return regions
[docs] def setToParallel(self): """Use the parallel times instead of the serial. Used to reset the behaviour after it has been set by the constructor""" if self.parallel: warning(self.name,"is already in parallel mode") else: self.parallel=True if self.processorDirs(): self.dirPrefix = self.processorDirs()[0] self.reread(force=True)
[docs] def addLocalConfig(self): """Add the local configuration file of the case to the configuration""" fName=path.join(self.name,"LocalConfigPyFoam") if path.exists(fName): conf().addFile(fName)
def __len__(self): self.reread() return len(self.times) def __contains__(self,item): self.reread() if self.timeName(item)!=None: return True else: return False def __getitem__(self,key): self.reread() ind=self.timeName(key) if ind==None: raise KeyError(key) else: return TimeDirectory(self.name, self.fullPath(ind), region=self.region) def __setitem__(self,key,value): self.reread() if type(key)!=str: raise TypeError(type(key),"of",key,"is not 'str'") if type(value)!=TimeDirectory: raise TypeError(type(value),"is not TimeDirectory") dest=TimeDirectory(self.name, self.fullPath(key), create=True,region=self.region) dest.copy(value) self.reread(force=True) def __delitem__(self,key): self.reread() nm=self.timeName(key) if nm==None: raise KeyError(key) self.rmtree(path.join(self.name, self.fullPath(nm)),ignore_errors=True) self.reread(force=True) def __iter__(self): self.reread() for key in self.times: yield TimeDirectory(self.name, self.fullPath(key), region=self.region, tolerant=self.tolerant)
[docs] def timeName(self,item,minTime=False): """Finds the name of a directory that corresponds with the given parameter :param item: the time that should be found :param minTime: search for the time with the minimal difference. Otherwise an exact match will be searched""" if type(item)==int: return self.times[item] else: ind=self.timeIndex(item,minTime) if ind==None: return None else: return self.times[ind]
[docs] def timeIndex(self,item,minTime=False): """Finds the index of a directory that corresponds with the given parameter :param item: the time that should be found :param minTime: search for the time with the minimal difference. Otherwise an exact match will be searched""" self.reread() time=float(item) result=None if minTime: result=0 for i in range(1,len(self.times)): if abs(float(self.times[result])-time)>abs(float(self.times[i])-time): result=i else: for i in range(len(self.times)): t=self.times[i] if abs(float(t)-time)<1e-6: if result==None: result=i elif abs(float(t)-time)<abs(float(self.times[result])-time): result=i return result
[docs] def fullPath(self,time): if self.dirPrefix: return path.join(self.dirPrefix, time) return time
[docs] def isValid(self): """Checks whether this is a valid case directory by looking for the system- and constant-directories and the controlDict-file""" return len(self.missingFiles())==0
[docs] def missingFiles(self): """Return a list of all the missing files and directories that are needed for a valid case""" missing=[] if not path.exists(self.systemDir()): missing.append(self.systemDir()) elif not path.isdir(self.systemDir()): missing.append(self.systemDir()) if not path.exists(self.constantDir()): missing.append(self.constantDir()) elif not path.isdir(self.constantDir()): missing.append(self.constantDir()) if not path.exists(self.controlDict()) and not path.exists(self.controlDict()+".gz"): missing.append(self.controlDict()) return missing
[docs] def addToClone(self,name): """add directory to the list that is needed to clone this case :param name: name of the subdirectory (the case directory is prepended)""" if path.exists(path.join(self.name,name)): self.essential.add(path.join(self.name,name)) elif self.parallel: if path.exists(path.join(self.name,"processor0",name)): self.essential.add(path.join(self.name,name)) else: # check whether this is a file pattern for f in glob.glob(path.join(self.name,name)): # no check for existence necessary self.essential.add(f)
[docs] def cloneCase(self, name, svnRemove=True, paraviewLink=True, followSymlinks=False): """create a clone of this case directory. Remove the target directory, if it already exists :param name: Name of the new case directory :param svnRemove: Look for .svn-directories and remove them :param followSymlinks: Follow symbolic links instead of just copying them :rtype: :class:`SolutionDirectory` or correct subclass :return: The target directory""" additional=eval(conf().get("Cloning","addItem")) for a in additional: self.addToClone(a) if path.exists(name): self.rmtree(name) mkdir(name) if self.parallel: for i in range(self.nrProcs()): mkdir(path.join(name,"processor%d" % i)) for d in self.essential: if d!=None: fs=followSymlinks if fs: noForce=eval(conf().get("Cloning","noForceSymlink")) pth,fl=path.split(d) for n in noForce: if fnmatch.fnmatch(fl,n): fs=False break if self.parallel: pth,fl=path.split(d) if path.exists(path.join(pth,"processor0",fl)): for i in range(self.nrProcs()): self.copytree(path.join(pth,"processor%d" % i,fl), path.join(name,"processor%d" % i), symlinks=not fs) if path.exists(d): self.copytree(d,name,symlinks=not fs) if svnRemove: self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune") return self.__class__(name, paraviewLink=paraviewLink, archive=self.archive)
[docs] def symlinkCase(self, name, followSymlinks=False, maxLevel=1, relPath=False): """create a clone of this case directory by creating a directory with symbolic links :param name: Name of the new case directory :param maxLevel: Maximum level down to which directories are created instead of symbolically linked :param followSymlinks: Follow symbolic links instead of just copying them :param relPath: the created symbolic links are relative (instead of absolute) :rtype: :class:`SolutionDirectory` or correct subclass :return: The target directory """ here=path.abspath(self.name) polyDirs=[path.relpath(p,here) for p in self.find("polyMesh*",here)] additional=eval(conf().get("Cloning","addItem")) for a in additional: self.addToClone(a) if path.exists(name): self.rmtree(name) mkdir(name) toProcess=[] for d in self.essential: if d!=None: if self.parallel: pth,fl=path.split(d) if path.exists(path.join(pth,"processor0",fl)): for i in range(self.nrProcs()): toProcess.append("processor%d" % i) if path.exists(d): toProcess.append(path.relpath(d,here)) maxLevel=max(0,maxLevel) self.__symlinkDir(src=here, dest=path.abspath(name), toProcess=toProcess, maxLevel=maxLevel, relPath=relPath, polyDirs=polyDirs, symlinks=not followSymlinks) return self.__class__(name,archive=self.archive)
def __symlinkDir(self,src,dest,toProcess,maxLevel,relPath,polyDirs,symlinks): for f in toProcess: there=path.join(src,f) here=path.join(dest,f) if path.islink(there) and not symlinks: there=path.realpath(there) doSymlink=False done=False if not path.isdir(there): doSymlink=True if path.basename(src)=="polyMesh": if f not in ["blockMeshDict","blockMeshDict.gz"]: doSymlink=False else: poly=[p for p in polyDirs if p.split(path.sep)[0]==f] if maxLevel>0 or len(poly)>0: done=True mkdir(here) self.__symlinkDir(src=there,dest=here, toProcess=[p for p in os.listdir(there) if p[0]!='.'], maxLevel=max(0,maxLevel-1), relPath=relPath, polyDirs=[path.join(*p.split(path.sep)[1:]) for p in poly if len(p.split(path.sep))>1], symlinks=symlinks) else: doSymlink=True if not done: if doSymlink: if relPath: linkTo=path.relpath(there,dest) else: linkTo=path.abspath(there) os.symlink(linkTo,here) else: self.copytree(there,here,symlinks=symlinks)
[docs] def packCase(self,tarname, last=False, exclude=[], verbose=False, additional=[], base=None): """Packs all the important files into a compressed tarfile. Uses the essential-list and excludes the .svn-directories. Also excludes files ending with ~ :param tarname: the name of the tar-file :param last: add the last directory to the list of directories to be added :param exclude: List with additional glob filename-patterns to be excluded :param additional: List with additional glob filename-patterns that are to be added :param base: Different name that is to be used as the baseName for the case inside the tar""" ex=["*~",".svn"]+exclude members=list(self.essential) addClone=eval(conf().get("Cloning","addItem")) members+=addClone if last: if self.getLast()!=self.first: if verbose: print_("Adding last ",self.getLast()) members.append(self.latestDir()) for p in additional: for f in listdir(self.name): if (f not in members) and fnmatch.fnmatch(f,p): if verbose: print_("Adding additional",f) members.append(path.join(self.name,f)) tar=tarfile.open(tarname,"w:gz") for m in members: self.addToTar(tar,m, verbose=verbose, exclude=ex,base=base) additional=eval(conf().get("Cloning","addItem")) for a in additional: self.addToTar(tar, path.join(self.name,a), verbose=verbose, exclude=ex, base=base) tar.close()
[docs] def addToTar(self,tar,pattern, exclude=[], base=None, proc=None, verbose=False): """The workhorse for the packCase-method""" if base==None: base=path.basename(self.name) if self.parallel and proc is None: for p in self.processorDirs(): self.addToTar(tar, path.join(path.dirname(pattern),p,path.basename(pattern)), exclude=exclude, base=base, verbose=verbose, proc=p) for name in glob.glob(pattern): excluded=False for e in exclude: if fnmatch.fnmatch(path.basename(name),e): excluded=True if excluded: continue if path.isdir(name): for m in listdir(name): self.addToTar(tar, path.join(name,m), exclude=exclude, verbose=verbose, proc=proc, base=base) else: arcname=path.join(base,name[len(self.name)+1:]) if path.islink(name): # if the symbolic link points to a file in the case keep it # otherwise replace with the real file lPath=path.os.readlink(name) if not path.isabs(lPath): rPath=path.realpath(name) common=path.commonprefix([path.abspath(rPath), path.abspath(base)]) # if the path is shorter than the base it must be outside the case if len(common)<len(path.abspath(base)): name=path.abspath(rPath) else: # use the abolute path name=lPath try: tar.getmember(arcname) # don't add ... the file is already there' except KeyError: # file not in tar if verbose: print_("Adding",name,"to tar") tar.add(name,arcname=arcname)
[docs] def getParallelTimes(self): """Get a list of the times in the processor0-directory""" result=[] proc0=path.join(self.name,"processor0") if path.exists(proc0): for f in listdir(proc0): try: val=float(f) result.append(f) except ValueError: pass result.sort(key=float) return result
[docs] def reread(self,force=False): """Rescan the directory for the time directories""" if force: del self.procDirs if not force and stat(self.name)[ST_CTIME]<=self.lastReread: return self.times=[] self.first=None self.firstParallel=None self.last=None procDirs = self.processorDirs() if len(procDirs) > 1: self.procNr = len(procDirs) elif len(procDirs) == 1: self.procNr = int(procDirs[0][len("processors"):]) else: self.procNr = 1 if procDirs and self.parallel: timesDir = path.join(self.name, procDirs[0]) else: timesDir = self.name for f in listdir(timesDir): try: val=float(f) self.times.append(f) except ValueError: pass self.lastReread=stat(self.name)[ST_CTIME] self.times.sort(key=float) if self.times: self.first = self.times[0] self.last = self.times[-1] if self.parallel and len(procDirs)>0: parTimes=[] for f in listdir(path.join(self.name, procDirs[0])): try: val=float(f) parTimes.append(f) except ValueError: pass if len(parTimes)>0: self.firstParallel=min(parTimes)
[docs] def processorDirs(self): """List with the processor directories""" try: return self.procDirs except: pass self.procDirs=[] for f in listdir(self.name): if re.compile("processor[0-9]+").match(f): self.procDirs.append(f) self.procDirs.sort(key=lambda x:int(x[len("processor"):])) if len(self.procDirs) == 0: tmp = [] for f in listdir(self.name): if re.compile("processors[0-9]+").match(f): tmp.append(f) if len(tmp) == 1: self.procDirs = tmp return self.procDirs
[docs] def nrProcs(self): """The number of directories with processor-data""" self.reread() return self.procNr
[docs] def getTimes(self): """ :return: List of all the available times""" self.reread() return self.times
[docs] def addBackup(self,pth): """add file to list of files that are to be copied to the archive""" self.backups.append(path.join(self.name,pth))
[docs] def getFirst(self): """:return: the first time for which a solution exists :rtype: str""" self.reread() return self.first
[docs] def getLast(self): """:return: the last time for which a solution exists :rtype: str""" self.reread() return self.last
[docs] def lastToArchive(self,name): """copy the last solution (plus the backup-files to the archive) :param name: name of the sub-directory in the archive""" if self.archive==None: print_("Warning: nor Archive-directory") return self.reread() fname=path.join(self.archive,name) if path.exists(fname): self.rmtree(fname) mkdir(fname) self.copytree(path.join(self.name,self.last),fname) for f in self.backups: self.copytree(f,fname)
[docs] def clearResults(self, after=None, removeProcs=False, keepLast=False, vtk=True, keepRegular=False, keepParallel=False, keepInterval=None, keepTimes=[], functionObjectData=False, dryRun=False, verbose=False, additional=[]): """remove all time-directories after a certain time. If not time ist set the initial time is used :param after: time after which directories ar to be removed :param removeProcs: if True the processorX-directories are removed. Otherwise the timesteps after last are removed from the processor-directories :param keepLast: Keep the data from the last timestep :param keepInterval: if set: keep timesteps that are this far apart :param vtk: Remove the VTK-directory if it exists :param keepRegular: keep all the times (only remove processor and other stuff) :param functionObjectData: tries do determine which data was written by function obejects and removes it :param additional: List with glob-patterns that are removed too""" self.reread() last=self.getLast() if after==None: try: time=float(self.first) except TypeError: warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing") return else: time=float(after) lastKeptIndex=int(-1e5) if keepInterval!=None: if keepInterval<=0: error("The keeping interval",keepInterval,"is smaller that 0") if not keepRegular: for f in self.times: keep=False if keepInterval!=None: thisIndex=int((float(f)+1e-10)/keepInterval) if thisIndex!=lastKeptIndex: keep=True for k in keepTimes: if self.timeIndex(float(f),True)==self.timeIndex(k,True): keep=True if float(f)>time and not (keepLast and f==last) and not keep: # print "Removing",path.join(self.name,f) if path.exists(path.join(self.name,f)): if verbose: print_("Clearing",path.join(self.name,f)) if not dryRun: self.rmtree(path.join(self.name,f)) elif keepInterval!=None: lastKeptIndex=int((float(f)+1e-10)/keepInterval) if path.exists(path.join(self.name,"VTK")) and vtk: if verbose: print_("Clearing",path.join(self.name,"VTK")) if not dryRun: self.rmtree(path.join(self.name,"VTK")) if self.nrProcs() and not keepParallel and not self.firstParallel is None: lastKeptIndex=int(-1e5) time=max(time,float(self.firstParallel)) for f in listdir(self.name): if re.compile("processor(|s)[0-9]+").match(f): if removeProcs: if verbose: print_("Clearing",path.join(self.name,f)) if not dryRun: self.rmtree(path.join(self.name,f)) else: pDir=path.join(self.name,f) for t in listdir(pDir): try: keep=False val=float(t) if keepInterval!=None: thisIndex=int((float(t)+1e-10)/keepInterval) if thisIndex!=lastKeptIndex: keep=True for k in keepTimes: if self.timeIndex(val,True)==self.timeIndex(k,True): keep=True if val>time and not (keepLast and t==last) and not keep: if path.exists(path.join(pDir,t)): if verbose: print_("Clearing",path.join(pDir,t)) if not dryRun: self.rmtree(path.join(pDir,t)) elif keepInterval!=None: lastKeptIndex=int((float(t)+1e-10)/keepInterval) except ValueError: pass if functionObjectData: cd=ParsedParameterFile(self.controlDict(),doMacroExpansion=True) if "functions" in cd: if type(cd["functions"]) in [DictProxy,dict]: for f in cd["functions"]: pth=path.join(self.name,f) if path.exists(pth): if verbose: print_("Clearing",pth) if not dryRun: self.rmtree(pth) else: for f in cd["functions"][0::2]: pth=path.join(self.name,f) if path.exists(pth): if verbose: print_("Clearing",pth) if not dryRun: self.rmtree(pth) additional+=eval(conf().get("Clearing","additionalpatterns")) for a in additional: self.clearPattern(a, dryRun=dryRun, verbose=verbose)
[docs] def clearPattern(self, globPat, dryRun=False, verbose=False): """Clear all files that fit a certain shell (glob) pattern :param glob: the pattern which the files are going to fit""" for f in glob.glob(path.join(self.name,globPat)): if verbose: print_("Clearing",f) if not dryRun: if path.isdir(f): self.rmtree(f,ignore_errors=False) else: os.unlink(f)
[docs] def clearOther(self, pyfoam=True, removeAnalyzed=False, verbose=False, dryRun=False, clearHistory=False, clearParameters=False): """Remove additional directories :param pyfoam: rremove all directories typically created by PyFoam""" if pyfoam: self.clearPattern("PyFoam.?*", dryRun=dryRun, verbose=verbose) if removeAnalyzed: self.clearPattern("*?.analyzed", dryRun=dryRun, verbose=verbose) if clearParameters: self.clearPattern("PyFoamPrepareCaseParameters", dryRun=dryRun, verbose=verbose) if clearHistory: self.clearPattern("PyFoamHistory", dryRun=dryRun, verbose=verbose)
[docs] def clear(self, after=None, processor=True, pyfoam=True, keepLast=False, vtk=True, verbose=False, keepRegular=False, keepParallel=False, keepInterval=None, keepTimes=[], removeAnalyzed=False, clearHistory=False, clearParameters=False, functionObjectData=False, dryRun=False, additional=[]): """One-stop-shop to remove data :param after: time after which directories ar to be removed :param processor: remove the processorXX directories :param pyfoam: rremove all directories typically created by PyFoam :param keepLast: Keep the last time-step :param additional: list with additional patterns to clear""" self.clearResults(after=after, removeProcs=processor, keepLast=keepLast, keepInterval=keepInterval, keepTimes=keepTimes, vtk=vtk, verbose=verbose, keepRegular=keepRegular, keepParallel=keepParallel, functionObjectData=functionObjectData, dryRun=dryRun, additional=additional) self.clearOther(pyfoam=pyfoam, removeAnalyzed=removeAnalyzed, clearParameters=clearParameters, clearHistory=clearHistory, dryRun=dryRun, verbose=verbose)
[docs] def initialDir(self): """:return: the name of the first time-directory (==initial conditions) :rtype: str""" self.reread() if self.first: return path.join(self.name,self.first) else: if path.exists(path.join(self.name,"0.org")): return path.join(self.name,"0.org") else: return None
[docs] def latestDir(self): """:return: the name of the first last-directory (==simulation results) :rtype: str""" self.reread() last=self.getLast() if last: return path.join(self.name,last) else: return None
[docs] def constantDir(self,region=None,processor=None): """:param region: Specify the region for cases with more than 1 mesh :param processor: name of the processor directory :return: the name of the C{constant}-directory :rtype: str""" pre=self.name if processor!=None: if type(processor)==int: processor="processor%d" % processor pre=path.join(pre,processor) if region==None and self.region!=None: region=self.region if region: return path.join(pre,"constant",region) else: return path.join(pre,"constant")
[docs] def systemDir(self,region=None,noRegion=False): """:param region: Specify the region for cases with more than 1 mesh :return: the name of the C{system}-directory :rtype: str""" if region==None and self.region!=None: region=self.region if region and not noRegion: return path.join(self.name,"system",region) else: return path.join(self.name,"system")
[docs] def controlDict(self): """:return: the name of the C{controlDict} :rtype: str""" return path.join(self.systemDir(noRegion=True),"controlDict")
[docs] def polyMeshDir(self,region=None,time=None,processor=None): """:param region: Specify the region for cases with more than 1 mesh :return: the name of the C{polyMesh} :param time: Time for which the mesh should be looked at :param processor: Name of the processor directory for decomposed cases :rtype: str""" if region==None and self.region!=None: region=self.region if time==None: return path.join( self.constantDir( region=region, processor=processor), "polyMesh") else: return path.join( TimeDirectory(self.name, time, region=region, processor=processor).name, "polyMesh")
[docs] def boundaryDict(self,region=None,time=None,processor=None): """:param region: Specify the region for cases with more than 1 mesh :return: name of the C{boundary}-file :rtype: str""" if region==None and self.region!=None: region=self.region return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
[docs] def blockMesh(self,region=None): """:param region: Specify the region for cases with more than 1 mesh :return: the name of the C{blockMeshDict} if it exists. Returns an empty string if it doesn't :rtype: str""" if region==None and self.region!=None: region=self.region for d in [self.systemDir(region=region),self.polyMeshDir(region=region)]: p=path.join(d,"blockMeshDict") if path.exists(p): return p return ""
[docs] def makeFile(self,name): """create a file in the solution directory and return a corresponding BasicFile-object :param name: Name of the file :rtype: :class:`BasicFile`""" return BasicFile(path.join(self.name,name))
[docs] def getRegions(self,defaultRegion=False): """Gets a list of all the available mesh regions by checking all directories in constant and using all those that have a polyMesh-subdirectory :param defaultRegion: should the default region also be added (as None)""" lst=[] for d in self.listDirectory(self.constantDir()): if path.isdir(path.join(self.constantDir(),d)): if path.exists(self.polyMeshDir(region=d)): lst.append(d) if defaultRegion: if path.exists(self.polyMeshDir()): lst.append(None) lst.sort() return lst
[docs] def addToHistory(self,*text): """Adds a line with date and username to a file 'PyFoamHistory' that resides in the local directory""" hist=open(path.join(self.name,"PyFoamHistory"),"a") try: # this seems to fail when no stdin is available username=getlogin() except OSError: try: username=environ["USER"] except KeyError: username="unknown" hist.write("%s by %s in %s :" % (asctime(),username,uname()[1])) for t in text: hist.write(str(t)+" ") hist.write("\n") hist.close()
[docs] def listFiles(self,directory=None): """List all the plain files (not directories) in a subdirectory of the case :param directory: the subdirectory. If unspecified the case-directory itself is used :return: List with the plain filenames""" result=[] theDir=self.name if directory: theDir=path.join(theDir,directory) for f in listdir(theDir): if f[0]!='.' and f[-1]!='~': if path.isfile(path.join(theDir,f)): result.append(f) return result
[docs] def getDictionaryText(self,directory,name): """:param directory: Sub-directory of the case :param name: name of the dictionary file :return: the contents of the file as a big string""" result=None theDir=self.name if directory: theDir=path.join(theDir,directory) if path.exists(path.join(theDir,name)): result=open(path.join(theDir,name)).read() else: warning("File",name,"does not exist in directory",directory,"of case",self.name) return result
[docs] def writeDictionaryContents(self,directory,name,contents): """Writes the contents of a dictionary :param directory: Sub-directory of the case :param name: name of the dictionary file :param contents: Python-dictionary with the dictionary contents""" theDir=self.name if directory: theDir=path.join(theDir,directory) result=WriteParameterFile(path.join(theDir,name)) result.content=contents result.writeFile()
[docs] def writeDictionaryText(self,directory,name,text): """Writes the contents of a dictionary :param directory: Sub-directory of the case :param name: name of the dictionary file :param text: String with the dictionary contents""" theDir=self.name if directory: theDir=path.join(theDir,directory) result=open(path.join(theDir,name),"w").write(text)
[docs] def getDictionaryContents(self,directory,name): """:param directory: Sub-directory of the case :param name: name of the dictionary file :return: the contents of the file as a python data-structure""" result={} theDir=self.name if directory: theDir=path.join(theDir,directory) if path.exists(path.join(theDir,name)) or path.exists(path.join(theDir,name+".gz")): result=ParsedParameterFile(path.join(theDir,name)).content else: warning("File",name,"does not exist in directory",directory,"of case",self.name) return result
[docs] def determineVCS(self): """Find out whether this directory is controlled by a VCS and return the abbreviation of that VCS""" if path.isdir(path.join(self.name,".hg")): return "hg" elif path.isdir(path.join(self.name,".git")): return "git" elif path.isdir(path.join(self.name,".svn")): return "svn" else: return None
[docs] def addPostprocDir(self,dirName,fail=True): if dirName in self.__postprocDirs: return full=path.join(self.name,dirName) if not path.isdir(full): if fail: error(full,"does not exist or is no directory") else: return self.__postprocDirs.append(dirName) self.__postprocInfo={}
def __classifyDirectory(self,dPath): cnt=0 minimum="1e40" for d in listdir(dPath): full=path.join(dPath,d) if not path.isdir(full): continue try: if float(d)<float(minimum): minimum=d cnt+=1 except ValueError: continue if cnt<=0: return None first=path.join(dPath,minimum) hypothesis=None for f in listdir(first): ff=path.join(first,f) if not path.isfile(ff): continue try: float(f) continue except ValueError: pass b,e=path.splitext(f) if e==".xy": newHypothesis="sample" elif e==".vtk": newHypothesis="surface" elif e=="": if b.find("istribution")>0: newHypothesis="distribution" else: newHypothesis="timeline" else: newHypothesis=None if hypothesis==None: hypothesis=newHypothesis elif hypothesis!=newHypothesis and newHypothesis: error("Can not decide between",hypothesis, "and",newHypothesis,"for",full) return hypothesis def __scanForPostproc(self,dirName): for d in listdir(path.join(self.name,dirName)): full=path.join(self.name,dirName,d) if not path.isdir(full): continue try: # we don't want time directories float(d) continue except ValueError: pass c=self.__classifyDirectory(full) use=path.join(dirName,d) if c=="timeline": self.__postprocInfo["timelines"].append(use) elif c=="sample": self.__postprocInfo["samples"].append(use) elif c=="surface": self.__postprocInfo["surfaces"].append(use) elif c=="distribution": self.__postprocInfo["distributions"].append(use) elif c==None: pass else: error("Unknown classification",c,"for",full) # Pick up additional distributions certain swak-functionobjects generate if path.exists(path.join(full,"distributions")): c=self.__classifyDirectory(path.join(full,"distributions")) if c=="distribution": self.__postprocInfo["distributions"].append(path.join(use,"distributions")) def __scanPostproc(self): self.__postprocInfo={"timelines":[], "samples":[], "distributions":[], "surfaces":[]} for d in self.__postprocDirs: self.__scanForPostproc(d) @property def pickledData(self): """Get the pickled data files. Newest first""" dirAndTime=[] for f in ["pickledData","pickledUnfinishedData","pickledStartData"]: for g in glob.glob(path.join(self.name,"*.analyzed")): pName=path.join(g,f) if path.exists(pName): dirAndTime.append((path.getmtime(pName),pName)) dirAndTime.sort(key=lambda x:x[0],reverse=True) return [s[len(self.name)+1:] for t,s in dirAndTime] @property def pickledPlots(self): """Get the pickled plot files. Newest first""" dirAndTime=[] for g in glob.glob(path.join(self.name,"*.analyzed")): pName=path.join(g,"pickledPlots") if path.exists(pName): dirAndTime.append((path.getmtime(pName),pName)) dirAndTime.sort(key=lambda x:x[0],reverse=True) return [s[len(self.name)+1:] for t,s in dirAndTime] @property def timelines(self): """Return sub-directories that contain timeline-data""" if "timelines" not in self.__postprocInfo: self.__scanPostproc() return self.__postprocInfo["timelines"] @property def distributions(self): """Return sub-directories that contain distribution-data""" if "distributions" not in self.__postprocInfo: self.__scanPostproc() return self.__postprocInfo["distributions"] @property def samples(self): """Return sub-directories that contain sample-data""" if "samples" not in self.__postprocInfo: self.__scanPostproc() return self.__postprocInfo["samples"] @property def surfaces(self): if "surfaces" not in self.__postprocInfo: self.__scanPostproc() return self.__postprocInfo["surfaces"]
[docs] def getParametersFromFile(self): """Get Parameters from the file created by PrepareCase""" fName=path.join(self.name,"PyFoamPrepareCaseParameters") if path.exists(fName): return ParsedParameterFile(fName,noHeader=True).content else: return {}
[docs]class ChemkinSolutionDirectory(SolutionDirectory): """Solution directory with a directory for the Chemkin-files""" chemkinName = "chemkin" def __init__(self,name,archive="ArchiveDir"): SolutionDirectory.__init__(self,name,archive=archive) self.addToClone(self.chemkinName)
[docs] def chemkinDir(self): """:rtype: str :return: The directory with the Chemkin-Files""" return path.join(self.name,self.chemkinName)
[docs]class NoTouchSolutionDirectory(SolutionDirectory): """Convenience class that makes sure that nothing new is created""" def __init__(self, name, region=None): SolutionDirectory.__init__(self, name, archive=None, paraviewLink=False, region=region)
# Should work with Python3 and Python2