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

Source Code for Module PyFoam.RunDictionary.SolutionDirectory

  1  #  ICE Revision: $Id: SolutionDirectory.py 10967 2009-10-19 16:02:07Z fpoll $  
  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  from ParsedParameterFile import ParsedParameterFile,WriteParameterFile 
 10   
 11  from os import listdir,path,mkdir,symlink,stat,getlogin,uname,environ 
 12  from time import asctime 
 13  from stat import ST_CTIME 
 14  import tarfile,fnmatch 
 15  import re 
 16   
17 -class SolutionDirectory(Utilities):
18 """Represents a solution directory 19 20 In the solution directory subdirectories whose names are numbers 21 are assumed to be solutions for a specific time-step 22 23 A sub-directory (called the Archive) is created to which solution 24 data is copied""" 25
26 - def __init__(self, 27 name, 28 archive="ArchiveDir", 29 paraviewLink=True, 30 parallel=False, 31 region=None):
32 """@param name: Name of the solution directory 33 @param archive: name of the directory where the lastToArchive-method 34 should copy files, if None no archive is created 35 @param paraviewLink: Create a symbolic link controlDict.foam for paraview 36 @param parallel: use the first processor-subdirectory for the authorative information 37 @param region: Mesh region for multi-region cases""" 38 39 self.name=path.abspath(name) 40 self.archive=None 41 if archive!=None: 42 self.archive=path.join(name,archive) 43 if not path.exists(self.archive): 44 mkdir(self.archive) 45 46 self.region=region 47 self.backups=[] 48 49 self.parallel=parallel 50 51 self.lastReread=0L 52 self.reread() 53 54 self.dirPrefix='' 55 if self.processorDirs() and parallel: 56 self.dirPrefix = self.processorDirs()[0] 57 58 self.essential=[self.systemDir(), 59 self.constantDir(), 60 self.initialDir()] 61 self.addToClone("PyFoamHistory") 62 63 if paraviewLink and not path.exists(self.controlDict()+".foam"): 64 symlink(path.basename(self.controlDict()),self.controlDict()+".foam")
65
66 - def __len__(self):
67 self.reread() 68 return len(self.times)
69
70 - def __contains__(self,item):
71 self.reread() 72 73 if self.timeName(item)!=None: 74 return True 75 else: 76 return False
77
78 - def __getitem__(self,key):
79 self.reread() 80 81 ind=self.timeName(key) 82 if ind==None: 83 raise KeyError(key) 84 else: 85 return TimeDirectory(self.name, self.fullPath(ind), region=self.region)
86
87 - def __setitem__(self,key,value):
88 self.reread() 89 if type(key)!=str: 90 raise TypeError(type(key),"of",key,"is not 'str'") 91 92 if type(value)!=TimeDirectory: 93 raise TypeError(type(value),"is not TimeDirectory") 94 95 dest=TimeDirectory(self.name, self.fullPath(key), create=True,region=self.region) 96 dest.copy(value) 97 98 self.reread(force=True)
99
100 - def __delitem__(self,key):
101 self.reread() 102 nm=self.timeName(key) 103 if nm==None: 104 raise KeyError(key) 105 106 self.execute("rm -rf "+path.join(self.name, self.fullPath(nm))) 107 108 self.reread(force=True)
109
110 - def __iter__(self):
111 self.reread() 112 for key in self.times: 113 yield TimeDirectory(self.name, self.fullPath(key), region=self.region)
114
115 - def timeName(self,item,minTime=False):
116 """Finds the name of a directory that corresponds with the given parameter 117 @param item: the time that should be found 118 @param minTime: search for the time with the minimal difference. 119 Otherwise an exact match will be searched""" 120 121 if type(item)==int: 122 return self.times[item] 123 else: 124 ind=self.timeIndex(item,minTime) 125 if ind==None: 126 return None 127 else: 128 return self.times[ind]
129
130 - def timeIndex(self,item,minTime=False):
131 """Finds the index of a directory that corresponds with the given parameter 132 @param item: the time that should be found 133 @param minTime: search for the time with the minimal difference. 134 Otherwise an exact match will be searched""" 135 self.reread() 136 137 time=float(item) 138 result=None 139 140 if minTime: 141 result=0 142 for i in range(1,len(self.times)): 143 if abs(float(self.times[result])-time)>abs(float(self.times[i])-time): 144 result=i 145 else: 146 for i in range(len(self.times)): 147 t=self.times[i] 148 if abs(float(t)-time)<1e-6: 149 if result==None: 150 result=i 151 elif abs(float(t)-time)<abs(float(self.times[result])-time): 152 result=i 153 154 return result
155
156 - def fullPath(self,time):
157 if self.dirPrefix: 158 return path.join(self.dirPrefix, time) 159 return time
160
161 - def isValid(self):
162 """Checks whether this is a valid case directory by looking for 163 the system- and constant-directories and the controlDict-file""" 164 165 return len(self.missingFiles())==0
166
167 - def missingFiles(self):
168 """Return a list of all the missing files and directories that 169 are needed for a valid case""" 170 missing=[] 171 if not path.exists(self.systemDir()): 172 missing.append(self.systemDir()) 173 elif not path.isdir(self.systemDir()): 174 missing.append(self.systemDir()) 175 if not path.exists(self.constantDir()): 176 missing.append(self.constantDir()) 177 elif not path.isdir(self.constantDir()): 178 missing.append(self.constantDir()) 179 if not path.exists(self.controlDict()): 180 missing.append(self.controlDict()) 181 182 return missing
183
184 - def addToClone(self,name):
185 """add directory to the list that is needed to clone this case 186 @param name: name of the subdirectory (the case directory is prepended)""" 187 if path.exists(path.join(self.name,name)): 188 self.essential.append(path.join(self.name,name))
189
190 - def cloneCase(self,name,svnRemove=True,followSymlinks=False):
191 """create a clone of this case directory. Remove the target directory, if it already exists 192 193 @param name: Name of the new case directory 194 @param svnRemove: Look for .svn-directories and remove them 195 @param followSymlinks: Follow symbolic links instead of just copying them 196 @rtype: L{SolutionDirectory} or correct subclass 197 @return: The target directory""" 198 199 cpOptions="-R" 200 if followSymlinks: 201 cpOptions+=" -L" 202 203 if path.exists(name): 204 self.execute("rm -r "+name) 205 mkdir(name) 206 for d in self.essential: 207 if d!=None: 208 self.execute("cp "+cpOptions+" "+d+" "+name) 209 210 if svnRemove: 211 self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune") 212 213 return self.__class__(name,archive=self.archive)
214
215 - def packCase(self,tarname,last=False,exclude=[],additional=[],base=None):
216 """Packs all the important files into a compressed tarfile. 217 Uses the essential-list and excludes the .svn-directories. 218 Also excludes files ending with ~ 219 @param tarname: the name of the tar-file 220 @param last: add the last directory to the list of directories to be added 221 @param exclude: List with additional glob filename-patterns to be excluded 222 @param additional: List with additional glob filename-patterns 223 that are to be added 224 @param base: Different name that is to be used as the baseName for the case inside the tar""" 225 226 ex=["*~",".svn"]+exclude 227 members=self.essential[:] 228 if last: 229 if self.getLast()!=self.first: 230 members.append(self.latestDir()) 231 for p in additional: 232 for f in listdir(self.name): 233 if (f not in members) and fnmatch.fnmatch(f,p): 234 members.append(path.join(self.name,f)) 235 236 tar=tarfile.open(tarname,"w:gz") 237 238 for m in members: 239 self.addToTar(tar,m,exclude=ex,base=base) 240 241 tar.close()
242
243 - def addToTar(self,tar,name,exclude=[],base=None):
244 """The workhorse for the packCase-method""" 245 246 if base==None: 247 base=path.basename(self.name) 248 249 for e in exclude: 250 if fnmatch.fnmatch(path.basename(name),e): 251 return 252 253 if path.isdir(name): 254 for m in listdir(name): 255 self.addToTar(tar,path.join(name,m),exclude=exclude,base=base) 256 else: 257 arcname=path.join(base,name[len(self.name)+1:]) 258 tar.add(name,arcname=arcname)
259
260 - def getParallelTimes(self):
261 """Get a list of the times in the processor0-directory""" 262 result=[] 263 264 proc0=path.join(self.name,"processor0") 265 if path.exists(proc0): 266 for f in listdir(proc0): 267 try: 268 val=float(f) 269 result.append(f) 270 except ValueError: 271 pass 272 result.sort(self.sorttimes) 273 return result
274
275 - def reread(self,force=False):
276 """Rescan the directory for the time directories""" 277 278 if not force and stat(self.name)[ST_CTIME]<=self.lastReread: 279 return 280 281 self.times=[] 282 self.first=None 283 self.last=None 284 procDirs = self.processorDirs() 285 self.procNr=len(procDirs) 286 287 if procDirs and self.parallel: 288 timesDir = path.join(self.name, procDirs[0]) 289 else: 290 timesDir = self.name 291 292 for f in listdir(timesDir): 293 try: 294 val=float(f) 295 self.times.append(f) 296 except ValueError: 297 pass 298 299 self.lastReread=stat(self.name)[ST_CTIME] 300 301 self.times.sort(self.sorttimes) 302 if self.times: 303 self.first = self.times[0] 304 self.last = self.times[-1]
305
306 - def processorDirs(self):
307 """List with the processor directories""" 308 try: 309 return self.procDirs 310 except: 311 pass 312 self.procDirs=[] 313 for f in listdir(self.name): 314 if re.compile("processor[0-9]+").match(f): 315 self.procDirs.append(f) 316 317 return self.procDirs
318
319 - def nrProcs(self):
320 """The number of directories with processor-data""" 321 self.reread() 322 return self.procNr
323
324 - def sorttimes(self,x,y):
325 """Sort function for the solution files""" 326 if(float(x)==float(y)): 327 return 0 328 elif float(x)<float(y): 329 return -1 330 else: 331 return 1
332
333 - def getTimes(self):
334 """ @return: List of all the available times""" 335 self.reread() 336 return self.times
337
338 - def addBackup(self,pth):
339 """add file to list of files that are to be copied to the 340 archive""" 341 self.backups.append(path.join(self.name,pth))
342
343 - def getFirst(self):
344 """@return: the first time for which a solution exists 345 @rtype: str""" 346 self.reread() 347 return self.first
348
349 - def getLast(self):
350 """@return: the last time for which a solution exists 351 @rtype: str""" 352 self.reread() 353 return self.last
354
355 - def lastToArchive(self,name):
356 """copy the last solution (plus the backup-files to the 357 archive) 358 359 @param name: name of the sub-directory in the archive""" 360 if self.archive==None: 361 print "Warning: nor Archive-directory" 362 return 363 364 self.reread() 365 fname=path.join(self.archive,name) 366 if path.exists(fname): 367 self.execute("rm -r "+fname) 368 mkdir(fname) 369 self.execute("cp -r "+path.join(self.name,self.last)+" "+fname) 370 for f in self.backups: 371 self.execute("cp -r "+f+" "+fname)
372
373 - def clearResults(self,after=None,removeProcs=False,keepLast=False,vtk=True,keepRegular=False):
374 """remove all time-directories after a certain time. If not time ist 375 set the initial time is used 376 @param after: time after which directories ar to be removed 377 @param removeProcs: if True the processorX-directories are removed. 378 Otherwise the timesteps after last are removed from the 379 processor-directories 380 @param keepLast: Keep the data from the last timestep 381 @param vtk: Remove the VTK-directory if it exists 382 @param keepRegular: keep all the times (only remove processor and other stuff)""" 383 384 self.reread() 385 386 last=self.getLast() 387 388 if after==None: 389 try: 390 time=float(self.first) 391 except TypeError: 392 warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing") 393 return 394 else: 395 time=float(after) 396 397 if not keepRegular: 398 for f in self.times: 399 if float(f)>time and not (keepLast and f==last): 400 self.execute("rm -r "+path.join(self.name,f)) 401 402 if path.exists(path.join(self.name,"VTK")) and vtk: 403 self.execute("rm -r "+path.join(self.name,"VTK")) 404 405 if self.nrProcs(): 406 for f in listdir(self.name): 407 if re.compile("processor[0-9]+").match(f): 408 if removeProcs: 409 self.execute("rm -r "+path.join(self.name,f)) 410 else: 411 pDir=path.join(self.name,f) 412 for t in listdir(pDir): 413 try: 414 val=float(t) 415 if val>time: 416 self.execute("rm -r "+path.join(pDir,t)) 417 except ValueError: 418 pass
419
420 - def clearPattern(self,glob):
421 """Clear all files that fit a certain shell (glob) pattern 422 @param glob: the pattern which the files are going to fit""" 423 424 self.execute("rm -rf "+path.join(self.name,glob))
425
426 - def clearOther(self, 427 pyfoam=True, 428 clearHistory=False):
429 """Remove additional directories 430 @param pyfoam: rremove all directories typically created by PyFoam""" 431 432 if pyfoam: 433 self.clearPattern("PyFoam.?*") 434 self.clearPattern("*?.analyzed") 435 if clearHistory: 436 self.clearPattern("PyFoamHistory")
437
438 - def clear(self, 439 after=None, 440 processor=True, 441 pyfoam=True, 442 keepLast=False, 443 vtk=True, 444 keepRegular=False, 445 clearHistory=False):
446 """One-stop-shop to remove data 447 @param after: time after which directories ar to be removed 448 @param processor: remove the processorXX directories 449 @param pyfoam: rremove all directories typically created by PyFoam 450 @param keepLast: Keep the last time-step""" 451 self.clearResults(after=after, 452 removeProcs=processor, 453 keepLast=keepLast, 454 vtk=vtk, 455 keepRegular=keepRegular) 456 self.clearOther(pyfoam=pyfoam, 457 clearHistory=clearHistory)
458
459 - def initialDir(self):
460 """@return: the name of the first time-directory (==initial 461 conditions 462 @rtype: str""" 463 self.reread() 464 465 if self.first: 466 return path.join(self.name,self.first) 467 else: 468 return None
469
470 - def latestDir(self):
471 """@return: the name of the first last-directory (==simulation 472 results) 473 @rtype: str""" 474 self.reread() 475 476 last=self.getLast() 477 if last: 478 return path.join(self.name,last) 479 else: 480 return None
481
482 - def constantDir(self,region=None,processor=None):
483 """@param region: Specify the region for cases with more than 1 mesh 484 @param processor: name of the processor directory 485 @return: the name of the C{constant}-directory 486 @rtype: str""" 487 pre=self.name 488 if processor!=None: 489 pre=path.join(pre,processor) 490 491 if region==None and self.region!=None: 492 region=self.region 493 if region: 494 return path.join(pre,"constant",region) 495 else: 496 return path.join(pre,"constant")
497
498 - def systemDir(self,region=None):
499 """@param region: Specify the region for cases with more than 1 mesh 500 @return: the name of the C{system}-directory 501 @rtype: str""" 502 if region==None and self.region!=None: 503 region=self.region 504 if region: 505 return path.join(self.name,"system",region) 506 else: 507 return path.join(self.name,"system")
508
509 - def controlDict(self):
510 """@return: the name of the C{controlDict} 511 @rtype: str""" 512 return path.join(self.systemDir(),"controlDict")
513
514 - def polyMeshDir(self,region=None,time="constant",processor=None):
515 """@param region: Specify the region for cases with more than 1 mesh 516 @return: the name of the C{polyMesh} 517 @param time: Time for which the mesh should be looked at 518 @param processor: Name of the processor directory for decomposed cases 519 @rtype: str""" 520 if region==None and self.region!=None: 521 region=self.region 522 return path.join(self.constantDir(region=region,processor=processor),"polyMesh")
523
524 - def boundaryDict(self,region=None,time="constant",processor=None):
525 """@param region: Specify the region for cases with more than 1 mesh 526 @return: name of the C{boundary}-file 527 @rtype: str""" 528 if region==None and self.region!=None: 529 region=self.region 530 return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
531
532 - def blockMesh(self,region=None):
533 """@param region: Specify the region for cases with more than 1 mesh 534 @return: the name of the C{blockMeshDict} if it exists. Returns 535 an empty string if it doesn't 536 @rtype: str""" 537 if region==None and self.region!=None: 538 region=self.region 539 p=path.join(self.polyMeshDir(region=region),"blockMeshDict") 540 if path.exists(p): 541 return p 542 else: 543 return ""
544
545 - def makeFile(self,name):
546 """create a file in the solution directory and return a 547 corresponding BasicFile-object 548 549 @param name: Name of the file 550 @rtype: L{BasicFile}""" 551 return BasicFile(path.join(self.name,name))
552
553 - def getRegions(self):
554 """Gets a list of all the available mesh regions by checking all 555 directories in constant and using all those that have a polyMesh-subdirectory""" 556 lst=[] 557 for d in self.listDirectory(self.constantDir()): 558 if path.isdir(path.join(self.constantDir(),d)): 559 if path.exists(self.polyMeshDir(region=d)): 560 lst.append(d) 561 lst.sort() 562 return lst
563
564 - def addToHistory(self,*text):
565 """Adds a line with date and username to a file 'PyFoamHistory' 566 that resides in the local directory""" 567 hist=open(path.join(self.name,"PyFoamHistory"),"a") 568 569 try: 570 # this seems to fail when no stdin is available 571 username=getlogin() 572 except OSError: 573 username=environ["USER"] 574 575 hist.write("%s by %s in %s :" % (asctime(),username,uname()[1])) 576 577 for t in text: 578 hist.write(str(t)+" ") 579 580 hist.write("\n") 581 hist.close()
582
583 - def listFiles(self,directory=None):
584 """List all the plain files (not directories) in a subdirectory 585 of the case 586 @param directory: the subdirectory. If unspecified the 587 case-directory itself is used 588 @return: List with the plain filenames""" 589 590 result=[] 591 theDir=self.name 592 if directory: 593 theDir=path.join(theDir,directory) 594 595 for f in listdir(theDir): 596 if f[0]!='.' and f[-1]!='~': 597 if path.isfile(path.join(theDir,f)): 598 result.append(f) 599 600 return result
601
602 - def getDictionaryText(self,directory,name):
603 """@param directory: Sub-directory of the case 604 @param name: name of the dictionary file 605 @return: the contents of the file as a big string""" 606 607 result=None 608 theDir=self.name 609 if directory: 610 theDir=path.join(theDir,directory) 611 612 if path.exists(path.join(theDir,name)): 613 result=open(path.join(theDir,name)).read() 614 else: 615 warning("File",name,"does not exist in directory",directory,"of case",self.name) 616 617 return result
618
619 - def writeDictionaryContents(self,directory,name,contents):
620 """Writes the contents of a dictionary 621 @param directory: Sub-directory of the case 622 @param name: name of the dictionary file 623 @param contents: Python-dictionary with the dictionary contents""" 624 625 theDir=self.name 626 if directory: 627 theDir=path.join(theDir,directory) 628 629 result=WriteParameterFile(path.join(theDir,name)) 630 result.content=contents 631 result.writeFile()
632
633 - def writeDictionaryText(self,directory,name,text):
634 """Writes the contents of a dictionary 635 @param directory: Sub-directory of the case 636 @param name: name of the dictionary file 637 @param text: String with the dictionary contents""" 638 639 theDir=self.name 640 if directory: 641 theDir=path.join(theDir,directory) 642 643 result=open(path.join(theDir,name),"w").write(text)
644
645 - def getDictionaryContents(self,directory,name):
646 """@param directory: Sub-directory of the case 647 @param name: name of the dictionary file 648 @return: the contents of the file as a python data-structure""" 649 650 result={} 651 theDir=self.name 652 if directory: 653 theDir=path.join(theDir,directory) 654 655 if path.exists(path.join(theDir,name)): 656 result=ParsedParameterFile(path.join(theDir,name)).content 657 else: 658 warning("File",name,"does not exist in directory",directory,"of case",self.name) 659 660 return result
661
662 -class ChemkinSolutionDirectory(SolutionDirectory):
663 """Solution directory with a directory for the Chemkin-files""" 664 665 chemkinName = "chemkin" 666
667 - def __init__(self,name,archive="ArchiveDir"):
668 SolutionDirectory.__init__(self,name,archive=archive) 669 670 self.addToClone(self.chemkinName)
671
672 - def chemkinDir(self):
673 """@rtype: str 674 @return: The directory with the Chemkin-Files""" 675 676 return path.join(self.name,self.chemkinName)
677
678 -class NoTouchSolutionDirectory(SolutionDirectory):
679 """Convenience class that makes sure that nothing new is created""" 680
681 - def __init__(self, 682 name, 683 region=None):
684 SolutionDirectory.__init__(self, 685 name, 686 archive=None, 687 paraviewLink=False, 688 region=region)
689