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

Source Code for Module PyFoam.RunDictionary.SolutionDirectory

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