Package PyFoam :: Package RunDictionary :: Module SolutionDirectory
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.RunDictionary.SolutionDirectory

  1  #  ICE Revision: $Id: SolutionDirectory.py 10067 2009-03-02 09:39:42Z bgschaid $  
  2  """Working with a solution directory""" 
  3   
  4  from PyFoam.Basics.Utilities import Utilities 
  5  from PyFoam.Basics.BasicFile import BasicFile 
  6  from PyFoam.Error import warning 
  7   
  8  from TimeDirectory import TimeDirectory 
  9   
 10  from os import listdir,path,mkdir,symlink,stat 
 11  from stat import ST_CTIME 
 12  import tarfile,fnmatch 
 13  import re 
 14   
15 -class SolutionDirectory(Utilities):
16 """Represents a solution directory 17 18 In the solution directory subdirectories whose names are numbers 19 are assumed to be solutions for a specific time-step 20 21 A sub-directory (called the Archive) is created to which solution 22 data is copied""" 23
24 - def __init__(self,name,archive="ArchiveDir",paraviewLink=True,region=None):
25 """@param name: Name of the solution directory 26 @param archive: name of the directory where the lastToArchive-method 27 should copy files, if None no archive is created 28 @param paraviewLink: Create a symbolic link controlDict.foam for paraview 29 @param region: Mesh region for multi-region cases""" 30 31 self.name=path.abspath(name) 32 self.archive=None 33 if archive!=None: 34 self.archive=path.join(name,archive) 35 if not path.exists(self.archive): 36 mkdir(self.archive) 37 38 self.region=region 39 self.backups=[] 40 41 self.lastReread=0L 42 self.reread() 43 44 self.essential=[self.systemDir(),self.constantDir(),self.initialDir()] 45 46 if paraviewLink and not path.exists(self.controlDict()+".foam"): 47 symlink(path.basename(self.controlDict()),self.controlDict()+".foam")
48
49 - def __len__(self):
50 self.reread() 51 return len(self.times)
52
53 - def __contains__(self,item):
54 self.reread() 55 56 if self.timeName(item)!=None: 57 return True 58 else: 59 return False
60
61 - def __getitem__(self,key):
62 self.reread() 63 64 ind=self.timeName(key) 65 if ind==None: 66 raise KeyError(key) 67 else: 68 return TimeDirectory(self.name,ind,region=self.region)
69
70 - def __setitem__(self,key,value):
71 self.reread() 72 if type(key)!=str: 73 raise TypeError(type(key),"of",key,"is not 'str'") 74 75 if type(value)!=TimeDirectory: 76 raise TypeError(type(value),"is not TimeDirectory") 77 78 dest=TimeDirectory(self.name,key,create=True,region=self.region) 79 dest.copy(value) 80 81 self.reread(force=True)
82
83 - def __delitem__(self,key):
84 self.reread() 85 nm=self.timeName(key) 86 if nm==None: 87 raise KeyError(key) 88 89 self.execute("rm -rf "+path.join(self.name,nm)) 90 91 self.reread(force=True)
92
93 - def __iter__(self):
94 self.reread() 95 for key in self.times: 96 yield TimeDirectory(self.name,key,region=self.region)
97
98 - def timeName(self,item,minTime=False):
99 """Finds the name of a directory that corresponds with the given parameter 100 @param item: the time that should be found 101 @param minTime: search for the time with the minimal difference. 102 Otherwise an exact match will be searched""" 103 104 if type(item)==int: 105 return self.times[item] 106 else: 107 ind=self.timeIndex(item,minTime) 108 if ind==None: 109 return None 110 else: 111 return self.times[ind]
112
113 - def timeIndex(self,item,minTime=False):
114 """Finds the index of a directory that corresponds with the given parameter 115 @param item: the time that should be found 116 @param minTime: search for the time with the minimal difference. 117 Otherwise an exact match will be searched""" 118 self.reread() 119 120 time=float(item) 121 result=None 122 123 if minTime: 124 result=0 125 for i in range(1,len(self.times)): 126 if abs(float(self.times[result])-time)>abs(float(self.times[i])-time): 127 result=i 128 else: 129 for i in range(len(self.times)): 130 t=self.times[i] 131 if abs(float(t)-time)<1e-6: 132 if result==None: 133 result=i 134 elif abs(float(t)-time)<abs(float(self.times[result])-time): 135 result=i 136 137 return result
138
139 - def isValid(self):
140 """Checks whether this is a valid case directory by looking for 141 the system- and constant-directories and the controlDict-file""" 142 143 return len(self.missingFiles())==0
144
145 - def missingFiles(self):
146 """Return a list of all the missing files and directories that 147 are needed for a valid case""" 148 missing=[] 149 if not path.exists(self.systemDir()): 150 missing.append(self.systemDir()) 151 elif not path.isdir(self.systemDir()): 152 missing.append(self.systemDir()) 153 if not path.exists(self.constantDir()): 154 missing.append(self.constantDir()) 155 elif not path.isdir(self.constantDir()): 156 missing.append(self.constantDir()) 157 if not path.exists(self.controlDict()): 158 missing.append(self.controlDict()) 159 160 return missing
161
162 - def addToClone(self,name):
163 """add directory to the list that is needed to clone this case 164 @param name: name of the subdirectory (the case directory is prepended)""" 165 self.essential.append(path.join(self.name,name))
166
167 - def cloneCase(self,name,svnRemove=True,followSymlinks=False):
168 """create a clone of this case directory. Remove the target directory, if it already exists 169 170 @param name: Name of the new case directory 171 @param svnRemove: Look for .svn-directories and remove them 172 @param followSymlinks: Follow symbolic links instead of just copying them 173 @rtype: L{SolutionDirectory} or correct subclass 174 @return: The target directory""" 175 176 cpOptions="-R" 177 if followSymlinks: 178 cpOptions+=" -L" 179 180 if path.exists(name): 181 self.execute("rm -r "+name) 182 mkdir(name) 183 for d in self.essential: 184 self.execute("cp "+cpOptions+" "+d+" "+name) 185 186 if svnRemove: 187 self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune") 188 189 return self.__class__(name,archive=self.archive)
190
191 - def packCase(self,tarname,last=False,exclude=[],additional=[],base=None):
192 """Packs all the important files into a compressed tarfile. 193 Uses the essential-list and excludes the .svn-directories. 194 Also excludes files ending with ~ 195 @param tarname: the name of the tar-file 196 @param last: add the last directory to the list of directories to be added 197 @param exclude: List with additional glob filename-patterns to be excluded 198 @param additional: List with additional glob filename-patterns 199 that are to be added 200 @param base: Different name that is to be used as the baseName for the case inside the tar""" 201 202 ex=["*~",".svn"]+exclude 203 members=self.essential[:] 204 if last: 205 if self.getLast()!=self.first: 206 members.append(self.latestDir()) 207 for p in additional: 208 for f in listdir(self.name): 209 if (f not in members) and fnmatch.fnmatch(f,p): 210 members.append(path.join(self.name,f)) 211 212 tar=tarfile.open(tarname,"w:gz") 213 214 for m in members: 215 self.addToTar(tar,m,exclude=ex,base=base) 216 217 tar.close()
218
219 - def addToTar(self,tar,name,exclude=[],base=None):
220 """The workhorse for the packCase-method""" 221 222 if base==None: 223 base=path.basename(self.name) 224 225 for e in exclude: 226 if fnmatch.fnmatch(path.basename(name),e): 227 return 228 229 if path.isdir(name): 230 for m in listdir(name): 231 self.addToTar(tar,path.join(name,m),exclude=exclude,base=base) 232 else: 233 arcname=path.join(base,name[len(self.name)+1:]) 234 tar.add(name,arcname=arcname)
235
236 - def getParallelTimes(self):
237 """Get a list of the times in the processor0-directory""" 238 result=[] 239 240 proc0=path.join(self.name,"processor0") 241 if path.exists(proc0): 242 for f in listdir(proc0): 243 try: 244 val=float(f) 245 result.append(f) 246 except ValueError: 247 pass 248 result.sort(self.sorttimes) 249 return result
250
251 - def reread(self,force=False):
252 """Rescan the directory for the time directories""" 253 254 if not force and stat(self.name)[ST_CTIME]<=self.lastReread: 255 return 256 257 self.times=[] 258 self.first=None 259 self.last=None 260 self.procDirs=0 261 262 for f in listdir(self.name): 263 try: 264 val=float(f) 265 self.times.append(f) 266 if self.first==None: 267 self.first=f 268 else: 269 if float(f)<float(self.first): 270 self.first=f 271 if self.last==None: 272 self.last=f 273 else: 274 if float(f)>float(self.last): 275 self.last=f 276 277 except ValueError: 278 if re.compile("processor[0-9]+").match(f): 279 self.procDirs+=1 280 281 self.lastReread=stat(self.name)[ST_CTIME] 282 283 self.times.sort(self.sorttimes)
284
285 - def processorDirs(self):
286 """List with the processor directories""" 287 dirs=[] 288 for f in listdir(self.name): 289 if re.compile("processor[0-9]+").match(f): 290 dirs.append(f) 291 292 return dirs
293
294 - def nrProcs(self):
295 """The number of directories with processor-data""" 296 self.reread() 297 return self.procDirs
298
299 - def sorttimes(self,x,y):
300 """Sort function for the solution files""" 301 if(float(x)==float(y)): 302 return 0 303 elif float(x)<float(y): 304 return -1 305 else: 306 return 1
307
308 - def getTimes(self):
309 """ @return: List of all the available times""" 310 self.reread() 311 return self.times
312
313 - def addBackup(self,pth):
314 """add file to list of files that are to be copied to the 315 archive""" 316 self.backups.append(path.join(self.name,pth))
317
318 - def getLast(self):
319 """@return: the last time for which a solution exists 320 @rtype: str""" 321 self.reread() 322 return self.last
323
324 - def lastToArchive(self,name):
325 """copy the last solution (plus the backup-files to the 326 archive) 327 328 @param name: name of the sub-directory in the archive""" 329 if self.archive==None: 330 print "Warning: nor Archive-directory" 331 return 332 333 self.reread() 334 fname=path.join(self.archive,name) 335 if path.exists(fname): 336 self.execute("rm -r "+fname) 337 mkdir(fname) 338 self.execute("cp -r "+path.join(self.name,self.last)+" "+fname) 339 for f in self.backups: 340 self.execute("cp -r "+f+" "+fname)
341
342 - def clearResults(self,after=None,removeProcs=False,keepLast=False,vtk=True,keepRegular=False):
343 """remove all time-directories after a certain time. If not time ist 344 set the initial time is used 345 @param after: time after which directories ar to be removed 346 @param removeProcs: if True the processorX-directories are removed. 347 Otherwise the timesteps after last are removed from the 348 processor-directories 349 @param keepLast: Keep the data from the last timestep 350 @param vtk: Remove the VTK-directory if it exists 351 @param keepRegular: keep all the times (only remove processor and other stuff)""" 352 353 self.reread() 354 355 last=self.getLast() 356 357 if after==None: 358 try: 359 time=float(self.first) 360 except TypeError: 361 warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing") 362 return 363 else: 364 time=float(after) 365 366 if not keepRegular: 367 for f in self.times: 368 if float(f)>time and not (keepLast and f==last): 369 self.execute("rm -r "+path.join(self.name,f)) 370 371 if path.exists(path.join(self.name,"VTK")) and vtk: 372 self.execute("rm -r "+path.join(self.name,"VTK")) 373 374 if self.nrProcs(): 375 for f in listdir(self.name): 376 if re.compile("processor[0-9]+").match(f): 377 if removeProcs: 378 self.execute("rm -r "+path.join(self.name,f)) 379 else: 380 pDir=path.join(self.name,f) 381 for t in listdir(pDir): 382 try: 383 val=float(t) 384 if val>time: 385 self.execute("rm -r "+path.join(pDir,t)) 386 except ValueError: 387 pass
388
389 - def clearPattern(self,glob):
390 """Clear all files that fit a certain shell (glob) pattern 391 @param glob: the pattern which the files are going to fit""" 392 393 self.execute("rm -rf "+path.join(self.name,glob))
394
395 - def clearOther(self,pyfoam=True):
396 """Remove additional directories 397 @param pyfoam: rremove all directories typically created by PyFoam""" 398 399 if pyfoam: 400 self.clearPattern("PyFoam.?*") 401 self.clearPattern("*?.analyzed")
402
403 - def clear(self,after=None,processor=True,pyfoam=True,keepLast=False,vtk=True,keepRegular=False):
404 """One-stop-shop to remove data 405 @param after: time after which directories ar to be removed 406 @param processor: remove the processorXX directories 407 @param pyfoam: rremove all directories typically created by PyFoam 408 @param keepLast: Keep the last time-step""" 409 self.clearResults(after=after,removeProcs=processor,keepLast=keepLast,vtk=vtk,keepRegular=keepRegular) 410 self.clearOther(pyfoam=pyfoam)
411
412 - def initialDir(self):
413 """@return: the name of the first time-directory (==initial 414 conditions 415 @rtype: str""" 416 self.reread() 417 418 if self.first: 419 return path.join(self.name,self.first) 420 else: 421 return None
422
423 - def latestDir(self):
424 """@return: the name of the first last-directory (==simulation 425 results) 426 @rtype: str""" 427 self.reread() 428 429 last=self.getLast() 430 if last: 431 return path.join(self.name,last) 432 else: 433 return None
434
435 - def constantDir(self,region=None,processor=None):
436 """@param region: Specify the region for cases with more than 1 mesh 437 @param processor: name of the processor directory 438 @return: the name of the C{constant}-directory 439 @rtype: str""" 440 pre=self.name 441 if processor!=None: 442 pre=path.join(pre,processor) 443 444 if region==None and self.region!=None: 445 region=self.region 446 if region: 447 return path.join(pre,"constant",region) 448 else: 449 return path.join(pre,"constant")
450
451 - def systemDir(self,region=None):
452 """@param region: Specify the region for cases with more than 1 mesh 453 @return: the name of the C{system}-directory 454 @rtype: str""" 455 if region==None and self.region!=None: 456 region=self.region 457 if region: 458 return path.join(self.name,"system",region) 459 else: 460 return path.join(self.name,"system")
461
462 - def controlDict(self):
463 """@return: the name of the C{controlDict} 464 @rtype: str""" 465 return path.join(self.systemDir(),"controlDict")
466
467 - def polyMeshDir(self,region=None,time="constant",processor=None):
468 """@param region: Specify the region for cases with more than 1 mesh 469 @return: the name of the C{polyMesh} 470 @param time: Time for which the mesh should be looked at 471 @param processor: Name of the processor directory for decomposed cases 472 @rtype: str""" 473 if region==None and self.region!=None: 474 region=self.region 475 return path.join(self.constantDir(region=region,processor=processor),"polyMesh")
476
477 - def boundaryDict(self,region=None,time="constant",processor=None):
478 """@param region: Specify the region for cases with more than 1 mesh 479 @return: name of the C{boundary}-file 480 @rtype: str""" 481 if region==None and self.region!=None: 482 region=self.region 483 return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
484
485 - def blockMesh(self,region=None):
486 """@param region: Specify the region for cases with more than 1 mesh 487 @return: the name of the C{blockMeshDict} if it exists. Returns 488 an empty string if it doesn't 489 @rtype: str""" 490 if region==None and self.region!=None: 491 region=self.region 492 p=path.join(self.polyMeshDir(region=region),"blockMeshDict") 493 if path.exists(p): 494 return p 495 else: 496 return ""
497
498 - def makeFile(self,name):
499 """create a file in the solution directory and return a 500 corresponding BasicFile-object 501 502 @param name: Name of the file 503 @rtype: L{BasicFile}""" 504 return BasicFile(path.join(self.name,name))
505
506 - def getRegions(self):
507 """Gets a list of all the available mesh regions by checking all 508 directories in constant and using all those that have a polyMesh-subdirectory""" 509 lst=[] 510 for d in self.listDirectory(self.constantDir()): 511 if path.isdir(path.join(self.constantDir(),d)): 512 if path.exists(self.polyMeshDir(region=d)): 513 lst.append(d) 514 lst.sort() 515 return lst
516
517 -class ChemkinSolutionDirectory(SolutionDirectory):
518 """Solution directory with a directory for the Chemkin-files""" 519 520 chemkinName = "chemkin" 521
522 - def __init__(self,name,archive="ArchiveDir"):
523 SolutionDirectory.__init__(self,name,archive=archive) 524 525 self.addToClone(self.chemkinName)
526
527 - def chemkinDir(self):
528 """@rtype: str 529 @return: The directory with the Chemkin-Files""" 530 531 return path.join(self.name,self.chemkinName)
532