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