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

Source Code for Module PyFoam.RunDictionary.SolutionDirectory

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