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 6543 2010-05-09T10:03:18.281975Z 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
217 - def cloneCase(self,name,svnRemove=True,followSymlinks=False):
218 """create a clone of this case directory. Remove the target directory, if it already exists 219 220 @param name: Name of the new case directory 221 @param svnRemove: Look for .svn-directories and remove them 222 @param followSymlinks: Follow symbolic links instead of just copying them 223 @rtype: L{SolutionDirectory} or correct subclass 224 @return: The target directory""" 225 226 cpOptions="-R" 227 if followSymlinks: 228 cpOptions+=" -L" 229 230 if path.exists(name): 231 self.execute("rm -r "+name) 232 mkdir(name) 233 for d in self.essential: 234 if d!=None: 235 self.execute("cp "+cpOptions+" "+d+" "+name) 236 237 if svnRemove: 238 self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune") 239 240 return self.__class__(name,archive=self.archive)
241
242 - def packCase(self,tarname,last=False,exclude=[],additional=[],base=None):
243 """Packs all the important files into a compressed tarfile. 244 Uses the essential-list and excludes the .svn-directories. 245 Also excludes files ending with ~ 246 @param tarname: the name of the tar-file 247 @param last: add the last directory to the list of directories to be added 248 @param exclude: List with additional glob filename-patterns to be excluded 249 @param additional: List with additional glob filename-patterns 250 that are to be added 251 @param base: Different name that is to be used as the baseName for the case inside the tar""" 252 253 ex=["*~",".svn"]+exclude 254 members=self.essential[:] 255 if last: 256 if self.getLast()!=self.first: 257 members.append(self.latestDir()) 258 for p in additional: 259 for f in listdir(self.name): 260 if (f not in members) and fnmatch.fnmatch(f,p): 261 members.append(path.join(self.name,f)) 262 263 tar=tarfile.open(tarname,"w:gz") 264 265 for m in members: 266 self.addToTar(tar,m,exclude=ex,base=base) 267 268 tar.close()
269
270 - def addToTar(self,tar,name,exclude=[],base=None):
271 """The workhorse for the packCase-method""" 272 273 if base==None: 274 base=path.basename(self.name) 275 276 for e in exclude: 277 if fnmatch.fnmatch(path.basename(name),e): 278 return 279 280 if path.isdir(name): 281 for m in listdir(name): 282 self.addToTar(tar,path.join(name,m),exclude=exclude,base=base) 283 else: 284 arcname=path.join(base,name[len(self.name)+1:]) 285 tar.add(name,arcname=arcname)
286
287 - def getParallelTimes(self):
288 """Get a list of the times in the processor0-directory""" 289 result=[] 290 291 proc0=path.join(self.name,"processor0") 292 if path.exists(proc0): 293 for f in listdir(proc0): 294 try: 295 val=float(f) 296 result.append(f) 297 except ValueError: 298 pass 299 result.sort(self.sorttimes) 300 return result
301
302 - def reread(self,force=False):
303 """Rescan the directory for the time directories""" 304 305 if not force and stat(self.name)[ST_CTIME]<=self.lastReread: 306 return 307 308 self.times=[] 309 self.first=None 310 self.last=None 311 procDirs = self.processorDirs() 312 self.procNr=len(procDirs) 313 314 if procDirs and self.parallel: 315 timesDir = path.join(self.name, procDirs[0]) 316 else: 317 timesDir = self.name 318 319 for f in listdir(timesDir): 320 try: 321 val=float(f) 322 self.times.append(f) 323 except ValueError: 324 pass 325 326 self.lastReread=stat(self.name)[ST_CTIME] 327 328 self.times.sort(self.sorttimes) 329 if self.times: 330 self.first = self.times[0] 331 self.last = self.times[-1]
332
333 - def processorDirs(self):
334 """List with the processor directories""" 335 try: 336 return self.procDirs 337 except: 338 pass 339 self.procDirs=[] 340 for f in listdir(self.name): 341 if re.compile("processor[0-9]+").match(f): 342 self.procDirs.append(f) 343 344 return self.procDirs
345
346 - def nrProcs(self):
347 """The number of directories with processor-data""" 348 self.reread() 349 return self.procNr
350
351 - def sorttimes(self,x,y):
352 """Sort function for the solution files""" 353 if(float(x)==float(y)): 354 return 0 355 elif float(x)<float(y): 356 return -1 357 else: 358 return 1
359
360 - def getTimes(self):
361 """ @return: List of all the available times""" 362 self.reread() 363 return self.times
364
365 - def addBackup(self,pth):
366 """add file to list of files that are to be copied to the 367 archive""" 368 self.backups.append(path.join(self.name,pth))
369
370 - def getFirst(self):
371 """@return: the first time for which a solution exists 372 @rtype: str""" 373 self.reread() 374 return self.first
375
376 - def getLast(self):
377 """@return: the last time for which a solution exists 378 @rtype: str""" 379 self.reread() 380 return self.last
381
382 - def lastToArchive(self,name):
383 """copy the last solution (plus the backup-files to the 384 archive) 385 386 @param name: name of the sub-directory in the archive""" 387 if self.archive==None: 388 print "Warning: nor Archive-directory" 389 return 390 391 self.reread() 392 fname=path.join(self.archive,name) 393 if path.exists(fname): 394 self.execute("rm -r "+fname) 395 mkdir(fname) 396 self.execute("cp -r "+path.join(self.name,self.last)+" "+fname) 397 for f in self.backups: 398 self.execute("cp -r "+f+" "+fname)
399
400 - def clearResults(self, 401 after=None, 402 removeProcs=False, 403 keepLast=False, 404 vtk=True, 405 keepRegular=False, 406 functionObjectData=False):
407 """remove all time-directories after a certain time. If not time ist 408 set the initial time is used 409 @param after: time after which directories ar to be removed 410 @param removeProcs: if True the processorX-directories are removed. 411 Otherwise the timesteps after last are removed from the 412 processor-directories 413 @param keepLast: Keep the data from the last timestep 414 @param vtk: Remove the VTK-directory if it exists 415 @param keepRegular: keep all the times (only remove processor and other stuff) 416 @param functionObjectData: tries do determine which data was written by function obejects and removes it""" 417 418 self.reread() 419 420 last=self.getLast() 421 422 if after==None: 423 try: 424 time=float(self.first) 425 except TypeError: 426 warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing") 427 return 428 else: 429 time=float(after) 430 431 if not keepRegular: 432 for f in self.times: 433 if float(f)>time and not (keepLast and f==last): 434 self.execute("rm -r "+path.join(self.name,f)) 435 436 if path.exists(path.join(self.name,"VTK")) and vtk: 437 self.execute("rm -r "+path.join(self.name,"VTK")) 438 439 if self.nrProcs(): 440 for f in listdir(self.name): 441 if re.compile("processor[0-9]+").match(f): 442 if removeProcs: 443 self.execute("rm -r "+path.join(self.name,f)) 444 else: 445 pDir=path.join(self.name,f) 446 for t in listdir(pDir): 447 try: 448 val=float(t) 449 if val>time: 450 self.execute("rm -r "+path.join(pDir,t)) 451 except ValueError: 452 pass 453 454 if functionObjectData: 455 cd=ParsedParameterFile(self.controlDict()) 456 if "functions" in cd: 457 for f in cd["functions"][0::2]: 458 pth=path.join(self.name,f) 459 if path.exists(pth): 460 shutil.rmtree(pth)
461
462 - def clearPattern(self,glob):
463 """Clear all files that fit a certain shell (glob) pattern 464 @param glob: the pattern which the files are going to fit""" 465 466 self.execute("rm -rf "+path.join(self.name,glob))
467
468 - def clearOther(self, 469 pyfoam=True, 470 clearHistory=False):
471 """Remove additional directories 472 @param pyfoam: rremove all directories typically created by PyFoam""" 473 474 if pyfoam: 475 self.clearPattern("PyFoam.?*") 476 self.clearPattern("*?.analyzed") 477 if clearHistory: 478 self.clearPattern("PyFoamHistory")
479
480 - def clear(self, 481 after=None, 482 processor=True, 483 pyfoam=True, 484 keepLast=False, 485 vtk=True, 486 keepRegular=False, 487 clearHistory=False, 488 functionObjectData=False):
489 """One-stop-shop to remove data 490 @param after: time after which directories ar to be removed 491 @param processor: remove the processorXX directories 492 @param pyfoam: rremove all directories typically created by PyFoam 493 @param keepLast: Keep the last time-step""" 494 self.clearResults(after=after, 495 removeProcs=processor, 496 keepLast=keepLast, 497 vtk=vtk, 498 keepRegular=keepRegular, 499 functionObjectData=functionObjectData) 500 self.clearOther(pyfoam=pyfoam, 501 clearHistory=clearHistory)
502
503 - def initialDir(self):
504 """@return: the name of the first time-directory (==initial 505 conditions 506 @rtype: str""" 507 self.reread() 508 509 if self.first: 510 return path.join(self.name,self.first) 511 else: 512 return None
513
514 - def latestDir(self):
515 """@return: the name of the first last-directory (==simulation 516 results) 517 @rtype: str""" 518 self.reread() 519 520 last=self.getLast() 521 if last: 522 return path.join(self.name,last) 523 else: 524 return None
525
526 - def constantDir(self,region=None,processor=None):
527 """@param region: Specify the region for cases with more than 1 mesh 528 @param processor: name of the processor directory 529 @return: the name of the C{constant}-directory 530 @rtype: str""" 531 pre=self.name 532 if processor!=None: 533 if type(processor)==int: 534 processor="processor%d" % processor 535 pre=path.join(pre,processor) 536 537 if region==None and self.region!=None: 538 region=self.region 539 if region: 540 return path.join(pre,"constant",region) 541 else: 542 return path.join(pre,"constant")
543
544 - def systemDir(self,region=None):
545 """@param region: Specify the region for cases with more than 1 mesh 546 @return: the name of the C{system}-directory 547 @rtype: str""" 548 if region==None and self.region!=None: 549 region=self.region 550 if region: 551 return path.join(self.name,"system",region) 552 else: 553 return path.join(self.name,"system")
554
555 - def controlDict(self):
556 """@return: the name of the C{controlDict} 557 @rtype: str""" 558 return path.join(self.systemDir(),"controlDict")
559
560 - def polyMeshDir(self,region=None,time=None,processor=None):
561 """@param region: Specify the region for cases with more than 1 mesh 562 @return: the name of the C{polyMesh} 563 @param time: Time for which the mesh should be looked at 564 @param processor: Name of the processor directory for decomposed cases 565 @rtype: str""" 566 if region==None and self.region!=None: 567 region=self.region 568 if time==None: 569 return path.join( 570 self.constantDir( 571 region=region, 572 processor=processor), 573 "polyMesh") 574 else: 575 return path.join( 576 TimeDirectory(self.name, 577 time, 578 region=region, 579 processor=processor).name, 580 "polyMesh")
581
582 - def boundaryDict(self,region=None,time=None,processor=None):
583 """@param region: Specify the region for cases with more than 1 mesh 584 @return: name of the C{boundary}-file 585 @rtype: str""" 586 if region==None and self.region!=None: 587 region=self.region 588 return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
589
590 - def blockMesh(self,region=None):
591 """@param region: Specify the region for cases with more than 1 mesh 592 @return: the name of the C{blockMeshDict} if it exists. Returns 593 an empty string if it doesn't 594 @rtype: str""" 595 if region==None and self.region!=None: 596 region=self.region 597 p=path.join(self.polyMeshDir(region=region),"blockMeshDict") 598 if path.exists(p): 599 return p 600 else: 601 return ""
602
603 - def makeFile(self,name):
604 """create a file in the solution directory and return a 605 corresponding BasicFile-object 606 607 @param name: Name of the file 608 @rtype: L{BasicFile}""" 609 return BasicFile(path.join(self.name,name))
610
611 - def getRegions(self):
612 """Gets a list of all the available mesh regions by checking all 613 directories in constant and using all those that have a polyMesh-subdirectory""" 614 lst=[] 615 for d in self.listDirectory(self.constantDir()): 616 if path.isdir(path.join(self.constantDir(),d)): 617 if path.exists(self.polyMeshDir(region=d)): 618 lst.append(d) 619 lst.sort() 620 return lst
621
622 - def addToHistory(self,*text):
623 """Adds a line with date and username to a file 'PyFoamHistory' 624 that resides in the local directory""" 625 hist=open(path.join(self.name,"PyFoamHistory"),"a") 626 627 try: 628 # this seems to fail when no stdin is available 629 username=getlogin() 630 except OSError: 631 username=environ["USER"] 632 633 hist.write("%s by %s in %s :" % (asctime(),username,uname()[1])) 634 635 for t in text: 636 hist.write(str(t)+" ") 637 638 hist.write("\n") 639 hist.close()
640
641 - def listFiles(self,directory=None):
642 """List all the plain files (not directories) in a subdirectory 643 of the case 644 @param directory: the subdirectory. If unspecified the 645 case-directory itself is used 646 @return: List with the plain filenames""" 647 648 result=[] 649 theDir=self.name 650 if directory: 651 theDir=path.join(theDir,directory) 652 653 for f in listdir(theDir): 654 if f[0]!='.' and f[-1]!='~': 655 if path.isfile(path.join(theDir,f)): 656 result.append(f) 657 658 return result
659
660 - def getDictionaryText(self,directory,name):
661 """@param directory: Sub-directory of the case 662 @param name: name of the dictionary file 663 @return: the contents of the file as a big string""" 664 665 result=None 666 theDir=self.name 667 if directory: 668 theDir=path.join(theDir,directory) 669 670 if path.exists(path.join(theDir,name)): 671 result=open(path.join(theDir,name)).read() 672 else: 673 warning("File",name,"does not exist in directory",directory,"of case",self.name) 674 675 return result
676
677 - def writeDictionaryContents(self,directory,name,contents):
678 """Writes the contents of a dictionary 679 @param directory: Sub-directory of the case 680 @param name: name of the dictionary file 681 @param contents: Python-dictionary with the dictionary contents""" 682 683 theDir=self.name 684 if directory: 685 theDir=path.join(theDir,directory) 686 687 result=WriteParameterFile(path.join(theDir,name)) 688 result.content=contents 689 result.writeFile()
690
691 - def writeDictionaryText(self,directory,name,text):
692 """Writes the contents of a dictionary 693 @param directory: Sub-directory of the case 694 @param name: name of the dictionary file 695 @param text: String with the dictionary contents""" 696 697 theDir=self.name 698 if directory: 699 theDir=path.join(theDir,directory) 700 701 result=open(path.join(theDir,name),"w").write(text)
702
703 - def getDictionaryContents(self,directory,name):
704 """@param directory: Sub-directory of the case 705 @param name: name of the dictionary file 706 @return: the contents of the file as a python data-structure""" 707 708 result={} 709 theDir=self.name 710 if directory: 711 theDir=path.join(theDir,directory) 712 713 if path.exists(path.join(theDir,name)): 714 result=ParsedParameterFile(path.join(theDir,name)).content 715 else: 716 warning("File",name,"does not exist in directory",directory,"of case",self.name) 717 718 return result
719
720 -class ChemkinSolutionDirectory(SolutionDirectory):
721 """Solution directory with a directory for the Chemkin-files""" 722 723 chemkinName = "chemkin" 724
725 - def __init__(self,name,archive="ArchiveDir"):
726 SolutionDirectory.__init__(self,name,archive=archive) 727 728 self.addToClone(self.chemkinName)
729
730 - def chemkinDir(self):
731 """@rtype: str 732 @return: The directory with the Chemkin-Files""" 733 734 return path.join(self.name,self.chemkinName)
735
736 -class NoTouchSolutionDirectory(SolutionDirectory):
737 """Convenience class that makes sure that nothing new is created""" 738
739 - def __init__(self, 740 name, 741 region=None):
742 SolutionDirectory.__init__(self, 743 name, 744 archive=None, 745 paraviewLink=False, 746 region=region)
747