1
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,error
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
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 tolerant=False,
49 region=None):
50 """@param name: Name of the solution directory
51 @param archive: name of the directory where the lastToArchive-method
52 should copy files, if None no archive is created
53 @param paraviewLink: Create a symbolic link controlDict.foam for paraview
54 @param tolerant: do not fail for minor inconsistencies
55 @param parallel: use the first processor-subdirectory for the authorative information
56 @param region: Mesh region for multi-region cases"""
57
58 self.name=path.abspath(name)
59 self.archive=None
60 if archive!=None:
61 self.archive=path.join(name,archive)
62 if not path.exists(self.archive):
63 mkdir(self.archive)
64
65 self.region=region
66 self.backups=[]
67
68 self.parallel=parallel
69 self.tolerant=tolerant
70
71 self.lastReread=0
72 self.reread()
73
74 self.dirPrefix=''
75 if self.processorDirs() and parallel:
76 self.dirPrefix = self.processorDirs()[0]
77
78 self.essential=set([self.systemDir(),
79 self.constantDir(),
80 self.initialDir()])
81
82
83 self.addToClone("PyFoamHistory")
84 self.addToClone("customRegexp")
85 self.addToClone("LocalConfigPyFoam")
86
87
88 self.addToClone("Allclean")
89 self.addToClone("Allrun")
90
91
92 emptyFoamFile=path.join(self.name,path.basename(self.name)+".foam")
93 if paraviewLink and not path.exists(emptyFoamFile):
94 dummy=open(emptyFoamFile,"w")
95
96 if addLocalConfig:
97 self.addLocalConfig()
98
100 """Use the parallel times instead of the serial.
101
102 Used to reset the behaviour after it has been set by the constructor"""
103 if self.parallel:
104 warning(self.name,"is already in parallel mode")
105 else:
106 self.parallel=True
107 if self.processorDirs():
108 self.dirPrefix = self.processorDirs()[0]
109 self.reread(force=True)
110
116
120
122 self.reread()
123
124 if self.timeName(item)!=None:
125 return True
126 else:
127 return False
128
137
150
160
162 self.reread()
163 for key in self.times:
164 yield TimeDirectory(self.name,
165 self.fullPath(key),
166 region=self.region,
167 tolerant=self.tolerant)
168
170 """Finds the name of a directory that corresponds with the given parameter
171 @param item: the time that should be found
172 @param minTime: search for the time with the minimal difference.
173 Otherwise an exact match will be searched"""
174
175 if type(item)==int:
176 return self.times[item]
177 else:
178 ind=self.timeIndex(item,minTime)
179 if ind==None:
180 return None
181 else:
182 return self.times[ind]
183
185 """Finds the index of a directory that corresponds with the given parameter
186 @param item: the time that should be found
187 @param minTime: search for the time with the minimal difference.
188 Otherwise an exact match will be searched"""
189 self.reread()
190
191 time=float(item)
192 result=None
193
194 if minTime:
195 result=0
196 for i in range(1,len(self.times)):
197 if abs(float(self.times[result])-time)>abs(float(self.times[i])-time):
198 result=i
199 else:
200 for i in range(len(self.times)):
201 t=self.times[i]
202 if abs(float(t)-time)<1e-6:
203 if result==None:
204 result=i
205 elif abs(float(t)-time)<abs(float(self.times[result])-time):
206 result=i
207
208 return result
209
211 if self.dirPrefix:
212 return path.join(self.dirPrefix, time)
213 return time
214
216 """Checks whether this is a valid case directory by looking for
217 the system- and constant-directories and the controlDict-file"""
218
219 return len(self.missingFiles())==0
220
237
246
247 - def cloneCase(self,name,svnRemove=True,followSymlinks=False):
248 """create a clone of this case directory. Remove the target directory, if it already exists
249
250 @param name: Name of the new case directory
251 @param svnRemove: Look for .svn-directories and remove them
252 @param followSymlinks: Follow symbolic links instead of just copying them
253 @rtype: L{SolutionDirectory} or correct subclass
254 @return: The target directory"""
255
256 additional=eval(conf().get("Cloning","addItem"))
257 for a in additional:
258 self.addToClone(a)
259
260 cpOptions="-R"
261 if followSymlinks:
262 cpOptions+=" -L"
263
264 if path.exists(name):
265 self.rmtree(name)
266 mkdir(name)
267 if self.parallel:
268 for i in range(self.nrProcs()):
269 mkdir(path.join(name,"processor%d" % i))
270
271 for d in self.essential:
272 if d!=None:
273 if self.parallel:
274 pth,fl=path.split(d)
275 if path.exists(path.join(pth,"processor0",fl)):
276 for i in range(self.nrProcs()):
277 self.copytree(path.join(pth,"processor%d" % i,fl),
278 path.join(name,"processor%d" % i),
279 symlinks=not followSymlinks)
280
281 if path.exists(d):
282 self.copytree(d,name,symlinks=not followSymlinks)
283
284 if svnRemove:
285 self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune")
286
287 return self.__class__(name,archive=self.archive)
288
289 - def symlinkCase(self,
290 name,
291 followSymlinks=False,
292 maxLevel=1,
293 relPath=False):
294 """create a clone of this case directory by creating a
295 directory with symbolic links
296
297 @param name: Name of the new case directory
298 @param maxLevel: Maximum level down to which directories are created instead of symbolically linked
299 @param followSymlinks: Follow symbolic links instead of just copying them
300 @param relPath: the created symbolic links are relative (instead of absolute)
301 @rtype: L{SolutionDirectory} or correct subclass
302 @return: The target directory
303 """
304 here=path.abspath(self.name)
305 polyDirs=[path.relpath(p,here) for p in self.find("polyMesh*",here)]
306
307 additional=eval(conf().get("Cloning","addItem"))
308 for a in additional:
309 self.addToClone(a)
310
311 if path.exists(name):
312 self.rmtree(name)
313 mkdir(name)
314 toProcess=[]
315 for d in self.essential:
316 if d!=None:
317 if self.parallel:
318 pth,fl=path.split(d)
319 if path.exists(path.join(pth,"processor0",fl)):
320 for i in range(self.nrProcs()):
321 toProcess.append("processor%d" % i)
322 if path.exists(d):
323 toProcess.append(path.relpath(d,here))
324
325 maxLevel=max(0,maxLevel)
326
327 self.__symlinkDir(src=here,
328 dest=path.abspath(name),
329 toProcess=toProcess,
330 maxLevel=maxLevel,
331 relPath=relPath,
332 polyDirs=polyDirs,
333 symlinks=not followSymlinks)
334
335 return self.__class__(name,archive=self.archive)
336
337 - def __symlinkDir(self,src,dest,toProcess,maxLevel,relPath,polyDirs,symlinks):
338 for f in toProcess:
339 there=path.join(src,f)
340 here=path.join(dest,f)
341 if path.islink(there) and not symlinks:
342 there=path.realpath(there)
343
344 doSymlink=False
345 done=False
346
347 if not path.isdir(there):
348 doSymlink=True
349 if path.basename(src)=="polyMesh":
350 if f not in ["blockMeshDict","blockMeshDict.gz"]:
351 doSymlink=False
352 else:
353 poly=[p for p in polyDirs if p.split(path.sep)[0]==f]
354 if maxLevel>0 or len(poly)>0:
355 done=True
356 mkdir(here)
357 self.__symlinkDir(src=there,dest=here,
358 toProcess=[p for p in os.listdir(there) if p[0]!='.'],
359 maxLevel=max(0,maxLevel-1),
360 relPath=relPath,
361 polyDirs=[path.join(*p.split(path.sep)[1:]) for p in poly if len(p.split(path.sep))>1],
362 symlinks=symlinks)
363 else:
364 doSymlink=True
365
366 if not done:
367 if doSymlink:
368 if relPath:
369 linkTo=path.relpath(there,dest)
370 else:
371 linkTo=path.abspath(there)
372 os.symlink(linkTo,here)
373 else:
374 self.copytree(there,here,symlinks=symlinks)
375
376 - def packCase(self,tarname,last=False,exclude=[],additional=[],base=None):
377 """Packs all the important files into a compressed tarfile.
378 Uses the essential-list and excludes the .svn-directories.
379 Also excludes files ending with ~
380 @param tarname: the name of the tar-file
381 @param last: add the last directory to the list of directories to be added
382 @param exclude: List with additional glob filename-patterns to be excluded
383 @param additional: List with additional glob filename-patterns
384 that are to be added
385 @param base: Different name that is to be used as the baseName for the case inside the tar"""
386
387 ex=["*~",".svn"]+exclude
388 members=list(self.essential)
389 if last:
390 if self.getLast()!=self.first:
391 members.append(self.latestDir())
392 for p in additional:
393 for f in listdir(self.name):
394 if (f not in members) and fnmatch.fnmatch(f,p):
395 members.append(path.join(self.name,f))
396
397 tar=tarfile.open(tarname,"w:gz")
398
399 for m in members:
400 self.addToTar(tar,m,exclude=ex,base=base)
401
402 additional=eval(conf().get("Cloning","addItem"))
403 for a in additional:
404 self.addToTar(tar,
405 path.join(self.name,a),
406 exclude=ex,
407 base=base)
408
409 tar.close()
410
411 - def addToTar(self,tar,name,exclude=[],base=None):
412 """The workhorse for the packCase-method"""
413
414 if base==None:
415 base=path.basename(self.name)
416
417 for e in exclude:
418 if fnmatch.fnmatch(path.basename(name),e):
419 return
420
421 if path.isdir(name):
422 for m in listdir(name):
423 self.addToTar(tar,path.join(name,m),exclude=exclude,base=base)
424 else:
425 arcname=path.join(base,name[len(self.name)+1:])
426 if path.islink(name):
427
428
429 lPath=path.os.readlink(name)
430 if not path.isabs(lPath):
431 rPath=path.realpath(name)
432 common=path.commonprefix([path.abspath(rPath),
433 path.abspath(base)])
434
435 if len(common)<len(path.abspath(base)):
436 name=path.abspath(rPath)
437 else:
438
439 name=lPath
440 tar.add(name,arcname=arcname)
441
443 """Get a list of the times in the processor0-directory"""
444 result=[]
445
446 proc0=path.join(self.name,"processor0")
447 if path.exists(proc0):
448 for f in listdir(proc0):
449 try:
450 val=float(f)
451 result.append(f)
452 except ValueError:
453 pass
454 result.sort(key=float)
455 return result
456
457 - def reread(self,force=False):
458 """Rescan the directory for the time directories"""
459
460 if not force and stat(self.name)[ST_CTIME]<=self.lastReread:
461 return
462
463 self.times=[]
464 self.first=None
465 self.last=None
466 procDirs = self.processorDirs()
467 self.procNr=len(procDirs)
468
469 if procDirs and self.parallel:
470 timesDir = path.join(self.name, procDirs[0])
471 else:
472 timesDir = self.name
473
474 for f in listdir(timesDir):
475 try:
476 val=float(f)
477 self.times.append(f)
478 except ValueError:
479 pass
480
481 self.lastReread=stat(self.name)[ST_CTIME]
482
483 self.times.sort(key=float)
484 if self.times:
485 self.first = self.times[0]
486 self.last = self.times[-1]
487
489 """List with the processor directories"""
490 try:
491 return self.procDirs
492 except:
493 pass
494 self.procDirs=[]
495 for f in listdir(self.name):
496 if re.compile("processor[0-9]+").match(f):
497 self.procDirs.append(f)
498
499 return self.procDirs
500
502 """The number of directories with processor-data"""
503 self.reread()
504 return self.procNr
505
507 """ @return: List of all the available times"""
508 self.reread()
509 return self.times
510
512 """add file to list of files that are to be copied to the
513 archive"""
514 self.backups.append(path.join(self.name,pth))
515
517 """@return: the first time for which a solution exists
518 @rtype: str"""
519 self.reread()
520 return self.first
521
523 """@return: the last time for which a solution exists
524 @rtype: str"""
525 self.reread()
526 return self.last
527
529 """copy the last solution (plus the backup-files to the
530 archive)
531
532 @param name: name of the sub-directory in the archive"""
533 if self.archive==None:
534 print_("Warning: nor Archive-directory")
535 return
536
537 self.reread()
538 fname=path.join(self.archive,name)
539 if path.exists(fname):
540 self.rmtree(fname)
541 mkdir(fname)
542 self.copytree(path.join(self.name,self.last),fname)
543 for f in self.backups:
544 self.copytree(f,fname)
545
546 - def clearResults(self,
547 after=None,
548 removeProcs=False,
549 keepLast=False,
550 vtk=True,
551 keepRegular=False,
552 keepParallel=False,
553 keepInterval=None,
554 functionObjectData=False,
555 additional=[]):
556 """remove all time-directories after a certain time. If not time ist
557 set the initial time is used
558 @param after: time after which directories ar to be removed
559 @param removeProcs: if True the processorX-directories are removed.
560 Otherwise the timesteps after last are removed from the
561 processor-directories
562 @param keepLast: Keep the data from the last timestep
563 @param keepInterval: if set: keep timesteps that are this far apart
564 @param vtk: Remove the VTK-directory if it exists
565 @param keepRegular: keep all the times (only remove processor and other stuff)
566 @param functionObjectData: tries do determine which data was written by function obejects and removes it
567 @param additional: List with glob-patterns that are removed too"""
568
569 self.reread()
570
571 last=self.getLast()
572
573 if after==None:
574 try:
575 time=float(self.first)
576 except TypeError:
577 warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing")
578 return
579 else:
580 time=float(after)
581
582 lastKeptIndex=int(-1e5)
583
584 if keepInterval!=None:
585 if keepInterval<=0:
586 error("The keeping interval",keepInterval,"is smaller that 0")
587
588 if not keepRegular:
589 for f in self.times:
590 keep=False
591 if keepInterval!=None:
592 thisIndex=int((float(f)+1e-10)/keepInterval)
593 if thisIndex!=lastKeptIndex:
594 keep=True
595 if float(f)>time and not (keepLast and f==last) and not keep:
596
597 self.rmtree(path.join(self.name,f))
598 elif keepInterval!=None:
599 lastKeptIndex=int((float(f)+1e-10)/keepInterval)
600
601 if path.exists(path.join(self.name,"VTK")) and vtk:
602 self.rmtree(path.join(self.name,"VTK"))
603
604 if self.nrProcs() and not keepParallel:
605 lastKeptIndex=int(-1e5)
606 for f in listdir(self.name):
607 if re.compile("processor[0-9]+").match(f):
608 if removeProcs:
609 self.rmtree(path.join(self.name,f))
610 else:
611 pDir=path.join(self.name,f)
612 for t in listdir(pDir):
613 try:
614 keep=False
615 val=float(t)
616 if keepInterval!=None:
617 thisIndex=int((float(f)+1e-10)/keepInterval)
618 if thisIndex!=lastKeptIndex:
619 keep=True
620 if val>time and not (keepLast and f==last) and not keep:
621 self.rmtree(path.join(pDir,t))
622 elif keepInterval!=None:
623 lastKeptIndex=int((float(f)+1e-10)/keepInterval)
624 except ValueError:
625 pass
626
627 if functionObjectData:
628 cd=ParsedParameterFile(self.controlDict())
629 if "functions" in cd:
630 if type(cd["functions"]) in [DictProxy,dict]:
631 for f in cd["functions"]:
632 pth=path.join(self.name,f)
633 if path.exists(pth):
634 self.rmtree(pth)
635 else:
636 for f in cd["functions"][0::2]:
637 pth=path.join(self.name,f)
638 if path.exists(pth):
639 self.rmtree(pth)
640
641 additional+=eval(conf().get("Clearing","additionalpatterns"))
642 for a in additional:
643 self.clearPattern(a)
644
646 """Clear all files that fit a certain shell (glob) pattern
647 @param glob: the pattern which the files are going to fit"""
648
649 for f in glob.glob(path.join(self.name,globPat)):
650 if path.isdir(f):
651 self.rmtree(f,ignore_errors=False)
652 else:
653 os.unlink(f)
654
655 - def clearOther(self,
656 pyfoam=True,
657 removeAnalyzed=False,
658 clearHistory=False):
659 """Remove additional directories
660 @param pyfoam: rremove all directories typically created by PyFoam"""
661
662 if pyfoam:
663 self.clearPattern("PyFoam.?*")
664 if removeAnalyzed:
665 self.clearPattern("*?.analyzed")
666 if clearHistory:
667 self.clearPattern("PyFoamHistory")
668
669 - def clear(self,
670 after=None,
671 processor=True,
672 pyfoam=True,
673 keepLast=False,
674 vtk=True,
675 keepRegular=False,
676 keepParallel=False,
677 keepInterval=None,
678 removeAnalyzed=False,
679 clearHistory=False,
680 functionObjectData=False,
681 additional=[]):
682 """One-stop-shop to remove data
683 @param after: time after which directories ar to be removed
684 @param processor: remove the processorXX directories
685 @param pyfoam: rremove all directories typically created by PyFoam
686 @param keepLast: Keep the last time-step
687 @param additional: list with additional patterns to clear"""
688 self.clearResults(after=after,
689 removeProcs=processor,
690 keepLast=keepLast,
691 keepInterval=keepInterval,
692 vtk=vtk,
693 keepRegular=keepRegular,
694 keepParallel=keepParallel,
695 functionObjectData=functionObjectData,
696 additional=additional)
697 self.clearOther(pyfoam=pyfoam,
698 removeAnalyzed=removeAnalyzed,
699 clearHistory=clearHistory)
700
702 """@return: the name of the first time-directory (==initial
703 conditions)
704 @rtype: str"""
705 self.reread()
706
707 if self.first:
708 return path.join(self.name,self.first)
709 else:
710 if path.exists(path.join(self.name,"0.org")):
711 return path.join(self.name,"0.org")
712 else:
713 return None
714
716 """@return: the name of the first last-directory (==simulation
717 results)
718 @rtype: str"""
719 self.reread()
720
721 last=self.getLast()
722 if last:
723 return path.join(self.name,last)
724 else:
725 return None
726
728 """@param region: Specify the region for cases with more than 1 mesh
729 @param processor: name of the processor directory
730 @return: the name of the C{constant}-directory
731 @rtype: str"""
732 pre=self.name
733 if processor!=None:
734 if type(processor)==int:
735 processor="processor%d" % processor
736 pre=path.join(pre,processor)
737
738 if region==None and self.region!=None:
739 region=self.region
740 if region:
741 return path.join(pre,"constant",region)
742 else:
743 return path.join(pre,"constant")
744
746 """@param region: Specify the region for cases with more than 1 mesh
747 @return: the name of the C{system}-directory
748 @rtype: str"""
749 if region==None and self.region!=None:
750 region=self.region
751 if region:
752 return path.join(self.name,"system",region)
753 else:
754 return path.join(self.name,"system")
755
757 """@return: the name of the C{controlDict}
758 @rtype: str"""
759 return path.join(self.systemDir(),"controlDict")
760
761 - def polyMeshDir(self,region=None,time=None,processor=None):
762 """@param region: Specify the region for cases with more than 1 mesh
763 @return: the name of the C{polyMesh}
764 @param time: Time for which the mesh should be looked at
765 @param processor: Name of the processor directory for decomposed cases
766 @rtype: str"""
767 if region==None and self.region!=None:
768 region=self.region
769 if time==None:
770 return path.join(
771 self.constantDir(
772 region=region,
773 processor=processor),
774 "polyMesh")
775 else:
776 return path.join(
777 TimeDirectory(self.name,
778 time,
779 region=region,
780 processor=processor).name,
781 "polyMesh")
782
783 - def boundaryDict(self,region=None,time=None,processor=None):
784 """@param region: Specify the region for cases with more than 1 mesh
785 @return: name of the C{boundary}-file
786 @rtype: str"""
787 if region==None and self.region!=None:
788 region=self.region
789 return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
790
792 """@param region: Specify the region for cases with more than 1 mesh
793 @return: the name of the C{blockMeshDict} if it exists. Returns
794 an empty string if it doesn't
795 @rtype: str"""
796 if region==None and self.region!=None:
797 region=self.region
798 p=path.join(self.polyMeshDir(region=region),"blockMeshDict")
799 if path.exists(p):
800 return p
801 else:
802 return ""
803
805 """create a file in the solution directory and return a
806 corresponding BasicFile-object
807
808 @param name: Name of the file
809 @rtype: L{BasicFile}"""
810 return BasicFile(path.join(self.name,name))
811
813 """Gets a list of all the available mesh regions by checking all
814 directories in constant and using all those that have a polyMesh-subdirectory
815 @param defaultRegion: should the default region also be added (as None)"""
816 lst=[]
817 for d in self.listDirectory(self.constantDir()):
818 if path.isdir(path.join(self.constantDir(),d)):
819 if path.exists(self.polyMeshDir(region=d)):
820 lst.append(d)
821
822 if defaultRegion:
823 if path.exists(self.polyMeshDir()):
824 lst.append(None)
825
826 lst.sort()
827 return lst
828
829 - def addToHistory(self,*text):
830 """Adds a line with date and username to a file 'PyFoamHistory'
831 that resides in the local directory"""
832 hist=open(path.join(self.name,"PyFoamHistory"),"a")
833
834 try:
835
836 username=getlogin()
837 except OSError:
838 username=environ["USER"]
839
840 hist.write("%s by %s in %s :" % (asctime(),username,uname()[1]))
841
842 for t in text:
843 hist.write(str(t)+" ")
844
845 hist.write("\n")
846 hist.close()
847
849 """List all the plain files (not directories) in a subdirectory
850 of the case
851 @param directory: the subdirectory. If unspecified the
852 case-directory itself is used
853 @return: List with the plain filenames"""
854
855 result=[]
856 theDir=self.name
857 if directory:
858 theDir=path.join(theDir,directory)
859
860 for f in listdir(theDir):
861 if f[0]!='.' and f[-1]!='~':
862 if path.isfile(path.join(theDir,f)):
863 result.append(f)
864
865 return result
866
867 - def getDictionaryText(self,directory,name):
868 """@param directory: Sub-directory of the case
869 @param name: name of the dictionary file
870 @return: the contents of the file as a big string"""
871
872 result=None
873 theDir=self.name
874 if directory:
875 theDir=path.join(theDir,directory)
876
877 if path.exists(path.join(theDir,name)):
878 result=open(path.join(theDir,name)).read()
879 else:
880 warning("File",name,"does not exist in directory",directory,"of case",self.name)
881
882 return result
883
884 - def writeDictionaryContents(self,directory,name,contents):
885 """Writes the contents of a dictionary
886 @param directory: Sub-directory of the case
887 @param name: name of the dictionary file
888 @param contents: Python-dictionary with the dictionary contents"""
889
890 theDir=self.name
891 if directory:
892 theDir=path.join(theDir,directory)
893
894 result=WriteParameterFile(path.join(theDir,name))
895 result.content=contents
896 result.writeFile()
897
898 - def writeDictionaryText(self,directory,name,text):
899 """Writes the contents of a dictionary
900 @param directory: Sub-directory of the case
901 @param name: name of the dictionary file
902 @param text: String with the dictionary contents"""
903
904 theDir=self.name
905 if directory:
906 theDir=path.join(theDir,directory)
907
908 result=open(path.join(theDir,name),"w").write(text)
909
910 - def getDictionaryContents(self,directory,name):
911 """@param directory: Sub-directory of the case
912 @param name: name of the dictionary file
913 @return: the contents of the file as a python data-structure"""
914
915 result={}
916 theDir=self.name
917 if directory:
918 theDir=path.join(theDir,directory)
919
920 if path.exists(path.join(theDir,name)):
921 result=ParsedParameterFile(path.join(theDir,name)).content
922 else:
923 warning("File",name,"does not exist in directory",directory,"of case",self.name)
924
925 return result
926
928 """Find out whether this directory is controlled by a VCS and
929 return the abbreviation of that VCS"""
930
931 if path.isdir(path.join(self.name,".hg")):
932 return "hg"
933 elif path.isdir(path.join(self.name,".git")):
934 return "git"
935 elif path.isdir(path.join(self.name,".svn")):
936 return "svn"
937 else:
938 return None
939
941 """Solution directory with a directory for the Chemkin-files"""
942
943 chemkinName = "chemkin"
944
945 - def __init__(self,name,archive="ArchiveDir"):
949
951 """@rtype: str
952 @return: The directory with the Chemkin-Files"""
953
954 return path.join(self.name,self.chemkinName)
955
957 """Convenience class that makes sure that nothing new is created"""
958
959 - def __init__(self,
960 name,
961 region=None):
967
968
969