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)
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):
112
114 """Use the parallel times instead of the serial.
115
116 Used to reset the behaviour after it has been set by the constructor"""
117 if self.parallel:
118 warning(self.name,"is already in parallel mode")
119 else:
120 self.parallel=True
121 if self.processorDirs():
122 self.dirPrefix = self.processorDirs()[0]
123 self.reread(force=True)
124
130
134
136 self.reread()
137
138 if self.timeName(item)!=None:
139 return True
140 else:
141 return False
142
151
164
174
176 self.reread()
177 for key in self.times:
178 yield TimeDirectory(self.name,
179 self.fullPath(key),
180 region=self.region,
181 tolerant=self.tolerant)
182
184 """Finds the name of a directory that corresponds with the given parameter
185 @param item: the time that should be found
186 @param minTime: search for the time with the minimal difference.
187 Otherwise an exact match will be searched"""
188
189 if type(item)==int:
190 return self.times[item]
191 else:
192 ind=self.timeIndex(item,minTime)
193 if ind==None:
194 return None
195 else:
196 return self.times[ind]
197
199 """Finds the index of a directory that corresponds with the given parameter
200 @param item: the time that should be found
201 @param minTime: search for the time with the minimal difference.
202 Otherwise an exact match will be searched"""
203 self.reread()
204
205 time=float(item)
206 result=None
207
208 if minTime:
209 result=0
210 for i in range(1,len(self.times)):
211 if abs(float(self.times[result])-time)>abs(float(self.times[i])-time):
212 result=i
213 else:
214 for i in range(len(self.times)):
215 t=self.times[i]
216 if abs(float(t)-time)<1e-6:
217 if result==None:
218 result=i
219 elif abs(float(t)-time)<abs(float(self.times[result])-time):
220 result=i
221
222 return result
223
225 if self.dirPrefix:
226 return path.join(self.dirPrefix, time)
227 return time
228
230 """Checks whether this is a valid case directory by looking for
231 the system- and constant-directories and the controlDict-file"""
232
233 return len(self.missingFiles())==0
234
251
265
266 - def cloneCase(self,name,svnRemove=True,followSymlinks=False):
267 """create a clone of this case directory. Remove the target directory, if it already exists
268
269 @param name: Name of the new case directory
270 @param svnRemove: Look for .svn-directories and remove them
271 @param followSymlinks: Follow symbolic links instead of just copying them
272 @rtype: L{SolutionDirectory} or correct subclass
273 @return: The target directory"""
274
275 additional=eval(conf().get("Cloning","addItem"))
276 for a in additional:
277 self.addToClone(a)
278
279 if path.exists(name):
280 self.rmtree(name)
281 mkdir(name)
282 if self.parallel:
283 for i in range(self.nrProcs()):
284 mkdir(path.join(name,"processor%d" % i))
285
286 for d in self.essential:
287 if d!=None:
288 fs=followSymlinks
289 if fs:
290 noForce=eval(conf().get("Cloning","noForceSymlink"))
291 pth,fl=path.split(d)
292 for n in noForce:
293 if fnmatch.fnmatch(fl,n):
294 fs=False
295 break
296
297 if self.parallel:
298 pth,fl=path.split(d)
299 if path.exists(path.join(pth,"processor0",fl)):
300 for i in range(self.nrProcs()):
301 self.copytree(path.join(pth,"processor%d" % i,fl),
302 path.join(name,"processor%d" % i),
303 symlinks=not fs)
304
305 if path.exists(d):
306 self.copytree(d,name,symlinks=not fs)
307
308 if svnRemove:
309 self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune")
310
311 return self.__class__(name,archive=self.archive)
312
313 - def symlinkCase(self,
314 name,
315 followSymlinks=False,
316 maxLevel=1,
317 relPath=False):
318 """create a clone of this case directory by creating a
319 directory with symbolic links
320
321 @param name: Name of the new case directory
322 @param maxLevel: Maximum level down to which directories are created instead of symbolically linked
323 @param followSymlinks: Follow symbolic links instead of just copying them
324 @param relPath: the created symbolic links are relative (instead of absolute)
325 @rtype: L{SolutionDirectory} or correct subclass
326 @return: The target directory
327 """
328 here=path.abspath(self.name)
329 polyDirs=[path.relpath(p,here) for p in self.find("polyMesh*",here)]
330
331 additional=eval(conf().get("Cloning","addItem"))
332 for a in additional:
333 self.addToClone(a)
334
335 if path.exists(name):
336 self.rmtree(name)
337 mkdir(name)
338 toProcess=[]
339 for d in self.essential:
340 if d!=None:
341 if self.parallel:
342 pth,fl=path.split(d)
343 if path.exists(path.join(pth,"processor0",fl)):
344 for i in range(self.nrProcs()):
345 toProcess.append("processor%d" % i)
346 if path.exists(d):
347 toProcess.append(path.relpath(d,here))
348
349 maxLevel=max(0,maxLevel)
350
351 self.__symlinkDir(src=here,
352 dest=path.abspath(name),
353 toProcess=toProcess,
354 maxLevel=maxLevel,
355 relPath=relPath,
356 polyDirs=polyDirs,
357 symlinks=not followSymlinks)
358
359 return self.__class__(name,archive=self.archive)
360
361 - def __symlinkDir(self,src,dest,toProcess,maxLevel,relPath,polyDirs,symlinks):
362 for f in toProcess:
363 there=path.join(src,f)
364 here=path.join(dest,f)
365 if path.islink(there) and not symlinks:
366 there=path.realpath(there)
367
368 doSymlink=False
369 done=False
370
371 if not path.isdir(there):
372 doSymlink=True
373 if path.basename(src)=="polyMesh":
374 if f not in ["blockMeshDict","blockMeshDict.gz"]:
375 doSymlink=False
376 else:
377 poly=[p for p in polyDirs if p.split(path.sep)[0]==f]
378 if maxLevel>0 or len(poly)>0:
379 done=True
380 mkdir(here)
381 self.__symlinkDir(src=there,dest=here,
382 toProcess=[p for p in os.listdir(there) if p[0]!='.'],
383 maxLevel=max(0,maxLevel-1),
384 relPath=relPath,
385 polyDirs=[path.join(*p.split(path.sep)[1:]) for p in poly if len(p.split(path.sep))>1],
386 symlinks=symlinks)
387 else:
388 doSymlink=True
389
390 if not done:
391 if doSymlink:
392 if relPath:
393 linkTo=path.relpath(there,dest)
394 else:
395 linkTo=path.abspath(there)
396 os.symlink(linkTo,here)
397 else:
398 self.copytree(there,here,symlinks=symlinks)
399
400 - def packCase(self,tarname,last=False,exclude=[],additional=[],base=None):
401 """Packs all the important files into a compressed tarfile.
402 Uses the essential-list and excludes the .svn-directories.
403 Also excludes files ending with ~
404 @param tarname: the name of the tar-file
405 @param last: add the last directory to the list of directories to be added
406 @param exclude: List with additional glob filename-patterns to be excluded
407 @param additional: List with additional glob filename-patterns
408 that are to be added
409 @param base: Different name that is to be used as the baseName for the case inside the tar"""
410
411 ex=["*~",".svn"]+exclude
412 members=list(self.essential)
413 if last:
414 if self.getLast()!=self.first:
415 members.append(self.latestDir())
416 for p in additional:
417 for f in listdir(self.name):
418 if (f not in members) and fnmatch.fnmatch(f,p):
419 members.append(path.join(self.name,f))
420
421 tar=tarfile.open(tarname,"w:gz")
422
423 for m in members:
424 self.addToTar(tar,m,exclude=ex,base=base)
425
426 additional=eval(conf().get("Cloning","addItem"))
427 for a in additional:
428 self.addToTar(tar,
429 path.join(self.name,a),
430 exclude=ex,
431 base=base)
432
433 tar.close()
434
435 - def addToTar(self,tar,pattern,exclude=[],base=None):
436 """The workhorse for the packCase-method"""
437
438 if base==None:
439 base=path.basename(self.name)
440
441 for name in glob.glob(pattern):
442 excluded=False
443 for e in exclude:
444 if fnmatch.fnmatch(path.basename(name),e):
445 excluded=True
446 if excluded:
447 continue
448
449 if path.isdir(name):
450 for m in listdir(name):
451 self.addToTar(tar,path.join(name,m),exclude=exclude,base=base)
452 else:
453 arcname=path.join(base,name[len(self.name)+1:])
454 if path.islink(name):
455
456
457 lPath=path.os.readlink(name)
458 if not path.isabs(lPath):
459 rPath=path.realpath(name)
460 common=path.commonprefix([path.abspath(rPath),
461 path.abspath(base)])
462
463 if len(common)<len(path.abspath(base)):
464 name=path.abspath(rPath)
465 else:
466
467 name=lPath
468 try:
469 tar.getmember(arcname)
470
471 except KeyError:
472
473 tar.add(name,arcname=arcname)
474
476 """Get a list of the times in the processor0-directory"""
477 result=[]
478
479 proc0=path.join(self.name,"processor0")
480 if path.exists(proc0):
481 for f in listdir(proc0):
482 try:
483 val=float(f)
484 result.append(f)
485 except ValueError:
486 pass
487 result.sort(key=float)
488 return result
489
490 - def reread(self,force=False):
491 """Rescan the directory for the time directories"""
492
493 if not force and stat(self.name)[ST_CTIME]<=self.lastReread:
494 return
495
496 self.times=[]
497 self.first=None
498 self.last=None
499 procDirs = self.processorDirs()
500 self.procNr=len(procDirs)
501
502 if procDirs and self.parallel:
503 timesDir = path.join(self.name, procDirs[0])
504 else:
505 timesDir = self.name
506
507 for f in listdir(timesDir):
508 try:
509 val=float(f)
510 self.times.append(f)
511 except ValueError:
512 pass
513
514 self.lastReread=stat(self.name)[ST_CTIME]
515
516 self.times.sort(key=float)
517 if self.times:
518 self.first = self.times[0]
519 self.last = self.times[-1]
520
522 """List with the processor directories"""
523 try:
524 return self.procDirs
525 except:
526 pass
527 self.procDirs=[]
528 for f in listdir(self.name):
529 if re.compile("processor[0-9]+").match(f):
530 self.procDirs.append(f)
531
532 return self.procDirs
533
535 """The number of directories with processor-data"""
536 self.reread()
537 return self.procNr
538
540 """ @return: List of all the available times"""
541 self.reread()
542 return self.times
543
545 """add file to list of files that are to be copied to the
546 archive"""
547 self.backups.append(path.join(self.name,pth))
548
550 """@return: the first time for which a solution exists
551 @rtype: str"""
552 self.reread()
553 return self.first
554
556 """@return: the last time for which a solution exists
557 @rtype: str"""
558 self.reread()
559 return self.last
560
562 """copy the last solution (plus the backup-files to the
563 archive)
564
565 @param name: name of the sub-directory in the archive"""
566 if self.archive==None:
567 print_("Warning: nor Archive-directory")
568 return
569
570 self.reread()
571 fname=path.join(self.archive,name)
572 if path.exists(fname):
573 self.rmtree(fname)
574 mkdir(fname)
575 self.copytree(path.join(self.name,self.last),fname)
576 for f in self.backups:
577 self.copytree(f,fname)
578
579 - def clearResults(self,
580 after=None,
581 removeProcs=False,
582 keepLast=False,
583 vtk=True,
584 keepRegular=False,
585 keepParallel=False,
586 keepInterval=None,
587 functionObjectData=False,
588 additional=[]):
589 """remove all time-directories after a certain time. If not time ist
590 set the initial time is used
591 @param after: time after which directories ar to be removed
592 @param removeProcs: if True the processorX-directories are removed.
593 Otherwise the timesteps after last are removed from the
594 processor-directories
595 @param keepLast: Keep the data from the last timestep
596 @param keepInterval: if set: keep timesteps that are this far apart
597 @param vtk: Remove the VTK-directory if it exists
598 @param keepRegular: keep all the times (only remove processor and other stuff)
599 @param functionObjectData: tries do determine which data was written by function obejects and removes it
600 @param additional: List with glob-patterns that are removed too"""
601
602 self.reread()
603
604 last=self.getLast()
605
606 if after==None:
607 try:
608 time=float(self.first)
609 except TypeError:
610 warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing")
611 return
612 else:
613 time=float(after)
614
615 lastKeptIndex=int(-1e5)
616
617 if keepInterval!=None:
618 if keepInterval<=0:
619 error("The keeping interval",keepInterval,"is smaller that 0")
620
621 if not keepRegular:
622 for f in self.times:
623 keep=False
624 if keepInterval!=None:
625 thisIndex=int((float(f)+1e-10)/keepInterval)
626 if thisIndex!=lastKeptIndex:
627 keep=True
628 if float(f)>time and not (keepLast and f==last) and not keep:
629
630 if path.exists(path.join(self.name,f)):
631 self.rmtree(path.join(self.name,f))
632 elif keepInterval!=None:
633 lastKeptIndex=int((float(f)+1e-10)/keepInterval)
634
635 if path.exists(path.join(self.name,"VTK")) and vtk:
636 self.rmtree(path.join(self.name,"VTK"))
637
638 if self.nrProcs() and not keepParallel:
639 lastKeptIndex=int(-1e5)
640 for f in listdir(self.name):
641 if re.compile("processor[0-9]+").match(f):
642 if removeProcs:
643 self.rmtree(path.join(self.name,f))
644 else:
645 pDir=path.join(self.name,f)
646 for t in listdir(pDir):
647 try:
648 keep=False
649 val=float(t)
650 if keepInterval!=None:
651 thisIndex=int((float(f)+1e-10)/keepInterval)
652 if thisIndex!=lastKeptIndex:
653 keep=True
654 if val>time and not (keepLast and t==last) and not keep:
655 if path.exists(path.join(pDir,t)):
656 self.rmtree(path.join(pDir,t))
657 elif keepInterval!=None:
658 lastKeptIndex=int((float(f)+1e-10)/keepInterval)
659 except ValueError:
660 pass
661
662 if functionObjectData:
663 cd=ParsedParameterFile(self.controlDict(),doMacroExpansion=True)
664 if "functions" in cd:
665 if type(cd["functions"]) in [DictProxy,dict]:
666 for f in cd["functions"]:
667 pth=path.join(self.name,f)
668 if path.exists(pth):
669 self.rmtree(pth)
670 else:
671 for f in cd["functions"][0::2]:
672 pth=path.join(self.name,f)
673 if path.exists(pth):
674 self.rmtree(pth)
675
676 additional+=eval(conf().get("Clearing","additionalpatterns"))
677 for a in additional:
678 self.clearPattern(a)
679
681 """Clear all files that fit a certain shell (glob) pattern
682 @param glob: the pattern which the files are going to fit"""
683
684 for f in glob.glob(path.join(self.name,globPat)):
685 if path.isdir(f):
686 self.rmtree(f,ignore_errors=False)
687 else:
688 os.unlink(f)
689
690 - def clearOther(self,
691 pyfoam=True,
692 removeAnalyzed=False,
693 clearHistory=False,
694 clearParameters=False):
695 """Remove additional directories
696 @param pyfoam: rremove all directories typically created by PyFoam"""
697
698 if pyfoam:
699 self.clearPattern("PyFoam.?*")
700 if removeAnalyzed:
701 self.clearPattern("*?.analyzed")
702 if clearParameters:
703 self.clearPattern("PyFoamPrepareCaseParameters")
704 if clearHistory:
705 self.clearPattern("PyFoamHistory")
706
707 - def clear(self,
708 after=None,
709 processor=True,
710 pyfoam=True,
711 keepLast=False,
712 vtk=True,
713 keepRegular=False,
714 keepParallel=False,
715 keepInterval=None,
716 removeAnalyzed=False,
717 clearHistory=False,
718 clearParameters=False,
719 functionObjectData=False,
720 additional=[]):
721 """One-stop-shop to remove data
722 @param after: time after which directories ar to be removed
723 @param processor: remove the processorXX directories
724 @param pyfoam: rremove all directories typically created by PyFoam
725 @param keepLast: Keep the last time-step
726 @param additional: list with additional patterns to clear"""
727 self.clearResults(after=after,
728 removeProcs=processor,
729 keepLast=keepLast,
730 keepInterval=keepInterval,
731 vtk=vtk,
732 keepRegular=keepRegular,
733 keepParallel=keepParallel,
734 functionObjectData=functionObjectData,
735 additional=additional)
736 self.clearOther(pyfoam=pyfoam,
737 removeAnalyzed=removeAnalyzed,
738 clearParameters=clearParameters,
739 clearHistory=clearHistory)
740
742 """@return: the name of the first time-directory (==initial
743 conditions)
744 @rtype: str"""
745 self.reread()
746
747 if self.first:
748 return path.join(self.name,self.first)
749 else:
750 if path.exists(path.join(self.name,"0.org")):
751 return path.join(self.name,"0.org")
752 else:
753 return None
754
756 """@return: the name of the first last-directory (==simulation
757 results)
758 @rtype: str"""
759 self.reread()
760
761 last=self.getLast()
762 if last:
763 return path.join(self.name,last)
764 else:
765 return None
766
768 """@param region: Specify the region for cases with more than 1 mesh
769 @param processor: name of the processor directory
770 @return: the name of the C{constant}-directory
771 @rtype: str"""
772 pre=self.name
773 if processor!=None:
774 if type(processor)==int:
775 processor="processor%d" % processor
776 pre=path.join(pre,processor)
777
778 if region==None and self.region!=None:
779 region=self.region
780 if region:
781 return path.join(pre,"constant",region)
782 else:
783 return path.join(pre,"constant")
784
786 """@param region: Specify the region for cases with more than 1 mesh
787 @return: the name of the C{system}-directory
788 @rtype: str"""
789 if region==None and self.region!=None:
790 region=self.region
791 if region:
792 return path.join(self.name,"system",region)
793 else:
794 return path.join(self.name,"system")
795
797 """@return: the name of the C{controlDict}
798 @rtype: str"""
799 return path.join(self.systemDir(),"controlDict")
800
801 - def polyMeshDir(self,region=None,time=None,processor=None):
802 """@param region: Specify the region for cases with more than 1 mesh
803 @return: the name of the C{polyMesh}
804 @param time: Time for which the mesh should be looked at
805 @param processor: Name of the processor directory for decomposed cases
806 @rtype: str"""
807 if region==None and self.region!=None:
808 region=self.region
809 if time==None:
810 return path.join(
811 self.constantDir(
812 region=region,
813 processor=processor),
814 "polyMesh")
815 else:
816 return path.join(
817 TimeDirectory(self.name,
818 time,
819 region=region,
820 processor=processor).name,
821 "polyMesh")
822
823 - def boundaryDict(self,region=None,time=None,processor=None):
824 """@param region: Specify the region for cases with more than 1 mesh
825 @return: name of the C{boundary}-file
826 @rtype: str"""
827 if region==None and self.region!=None:
828 region=self.region
829 return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
830
832 """@param region: Specify the region for cases with more than 1 mesh
833 @return: the name of the C{blockMeshDict} if it exists. Returns
834 an empty string if it doesn't
835 @rtype: str"""
836 if region==None and self.region!=None:
837 region=self.region
838 p=path.join(self.polyMeshDir(region=region),"blockMeshDict")
839 if path.exists(p):
840 return p
841 else:
842 return ""
843
845 """create a file in the solution directory and return a
846 corresponding BasicFile-object
847
848 @param name: Name of the file
849 @rtype: L{BasicFile}"""
850 return BasicFile(path.join(self.name,name))
851
853 """Gets a list of all the available mesh regions by checking all
854 directories in constant and using all those that have a polyMesh-subdirectory
855 @param defaultRegion: should the default region also be added (as None)"""
856 lst=[]
857 for d in self.listDirectory(self.constantDir()):
858 if path.isdir(path.join(self.constantDir(),d)):
859 if path.exists(self.polyMeshDir(region=d)):
860 lst.append(d)
861
862 if defaultRegion:
863 if path.exists(self.polyMeshDir()):
864 lst.append(None)
865
866 lst.sort()
867 return lst
868
869 - def addToHistory(self,*text):
870 """Adds a line with date and username to a file 'PyFoamHistory'
871 that resides in the local directory"""
872 hist=open(path.join(self.name,"PyFoamHistory"),"a")
873
874 try:
875
876 username=getlogin()
877 except OSError:
878 username=environ["USER"]
879
880 hist.write("%s by %s in %s :" % (asctime(),username,uname()[1]))
881
882 for t in text:
883 hist.write(str(t)+" ")
884
885 hist.write("\n")
886 hist.close()
887
889 """List all the plain files (not directories) in a subdirectory
890 of the case
891 @param directory: the subdirectory. If unspecified the
892 case-directory itself is used
893 @return: List with the plain filenames"""
894
895 result=[]
896 theDir=self.name
897 if directory:
898 theDir=path.join(theDir,directory)
899
900 for f in listdir(theDir):
901 if f[0]!='.' and f[-1]!='~':
902 if path.isfile(path.join(theDir,f)):
903 result.append(f)
904
905 return result
906
907 - def getDictionaryText(self,directory,name):
908 """@param directory: Sub-directory of the case
909 @param name: name of the dictionary file
910 @return: the contents of the file as a big string"""
911
912 result=None
913 theDir=self.name
914 if directory:
915 theDir=path.join(theDir,directory)
916
917 if path.exists(path.join(theDir,name)):
918 result=open(path.join(theDir,name)).read()
919 else:
920 warning("File",name,"does not exist in directory",directory,"of case",self.name)
921
922 return result
923
924 - def writeDictionaryContents(self,directory,name,contents):
925 """Writes the contents of a dictionary
926 @param directory: Sub-directory of the case
927 @param name: name of the dictionary file
928 @param contents: Python-dictionary with the dictionary contents"""
929
930 theDir=self.name
931 if directory:
932 theDir=path.join(theDir,directory)
933
934 result=WriteParameterFile(path.join(theDir,name))
935 result.content=contents
936 result.writeFile()
937
938 - def writeDictionaryText(self,directory,name,text):
939 """Writes the contents of a dictionary
940 @param directory: Sub-directory of the case
941 @param name: name of the dictionary file
942 @param text: String with the dictionary contents"""
943
944 theDir=self.name
945 if directory:
946 theDir=path.join(theDir,directory)
947
948 result=open(path.join(theDir,name),"w").write(text)
949
950 - def getDictionaryContents(self,directory,name):
951 """@param directory: Sub-directory of the case
952 @param name: name of the dictionary file
953 @return: the contents of the file as a python data-structure"""
954
955 result={}
956 theDir=self.name
957 if directory:
958 theDir=path.join(theDir,directory)
959
960 if path.exists(path.join(theDir,name)):
961 result=ParsedParameterFile(path.join(theDir,name)).content
962 else:
963 warning("File",name,"does not exist in directory",directory,"of case",self.name)
964
965 return result
966
968 """Find out whether this directory is controlled by a VCS and
969 return the abbreviation of that VCS"""
970
971 if path.isdir(path.join(self.name,".hg")):
972 return "hg"
973 elif path.isdir(path.join(self.name,".git")):
974 return "git"
975 elif path.isdir(path.join(self.name,".svn")):
976 return "svn"
977 else:
978 return None
979
980 - def addPostprocDir(self,dirName,fail=True):
981 if dirName in self.__postprocDirs:
982 return
983 full=path.join(self.name,dirName)
984 if not path.isdir(full):
985 if fail:
986 error(full,"does not exist or is no directory")
987 else:
988 return
989
990 self.__postprocDirs.append(dirName)
991 self.__postprocInfo={}
992
994 cnt=0
995 minimum="1e40"
996 for d in listdir(dPath):
997 full=path.join(dPath,d)
998 if not path.isdir(full):
999 continue
1000 try:
1001 if float(d)<float(minimum):
1002 minimum=d
1003 cnt+=1
1004 except ValueError:
1005 continue
1006 if cnt<=0:
1007 return None
1008 first=path.join(dPath,minimum)
1009 hypothesis=None
1010 for f in listdir(first):
1011 ff=path.join(first,f)
1012 if not path.isfile(ff):
1013 continue
1014 try:
1015 float(f)
1016 continue
1017 except ValueError:
1018 pass
1019 b,e=path.splitext(f)
1020 if e==".xy":
1021 newHypothesis="sample"
1022 elif e==".vtk":
1023 newHypothesis="surface"
1024 elif e=="":
1025 if b.find("istribution")>0:
1026 newHypothesis="distribution"
1027 else:
1028 newHypothesis="timeline"
1029 else:
1030 newHypothesis=None
1031
1032 if hypothesis==None:
1033 hypothesis=newHypothesis
1034 elif hypothesis!=newHypothesis and newHypothesis:
1035 error("Can not decide between",hypothesis,
1036 "and",newHypothesis,"for",full)
1037 return hypothesis
1038
1039 - def __scanForPostproc(self,dirName):
1040 for d in listdir(path.join(self.name,dirName)):
1041 full=path.join(self.name,dirName,d)
1042 if not path.isdir(full):
1043 continue
1044 try:
1045
1046 float(d)
1047 continue
1048 except ValueError:
1049 pass
1050 c=self.__classifyDirectory(full)
1051 use=path.join(dirName,d)
1052 if c=="timeline":
1053 self.__postprocInfo["timelines"].append(use)
1054 elif c=="sample":
1055 self.__postprocInfo["samples"].append(use)
1056 elif c=="surface":
1057 self.__postprocInfo["surfaces"].append(use)
1058 elif c=="distribution":
1059 self.__postprocInfo["distributions"].append(use)
1060 elif c==None:
1061 pass
1062 else:
1063 error("Unknown classification",c,"for",full)
1064
1065
1066 if path.exists(path.join(full,"distributions")):
1067 c=self.__classifyDirectory(path.join(full,"distributions"))
1068 if c=="distribution":
1069 self.__postprocInfo["distributions"].append(path.join(use,"distributions"))
1070
1071 - def __scanPostproc(self):
1072 self.__postprocInfo={"timelines":[],
1073 "samples":[],
1074 "distributions":[],
1075 "surfaces":[]}
1076 for d in self.__postprocDirs:
1077 self.__scanForPostproc(d)
1078
1079 @property
1081 """Get the pickled data files. Newest first"""
1082 dirAndTime=[]
1083 for f in ["pickledData","pickledUnfinishedData","pickledStartData"]:
1084 for g in glob.glob(path.join(self.name,"*.analyzed")):
1085 pName=path.join(g,f)
1086 if path.exists(pName):
1087 dirAndTime.append((path.getmtime(pName),pName))
1088 dirAndTime.sort(key=lambda x:x[0],reverse=True)
1089 return [s[len(self.name)+1:] for t,s in dirAndTime]
1090
1091 @property
1093 """Get the pickled plot files. Newest first"""
1094 dirAndTime=[]
1095 for g in glob.glob(path.join(self.name,"*.analyzed")):
1096 pName=path.join(g,"pickledPlots")
1097 if path.exists(pName):
1098 dirAndTime.append((path.getmtime(pName),pName))
1099 dirAndTime.sort(key=lambda x:x[0],reverse=True)
1100 return [s[len(self.name)+1:] for t,s in dirAndTime]
1101
1102 @property
1104 """Return sub-directories that contain timeline-data"""
1105 if "timelines" not in self.__postprocInfo:
1106 self.__scanPostproc()
1107 return self.__postprocInfo["timelines"]
1108
1109 @property
1111 """Return sub-directories that contain distribution-data"""
1112 if "distributions" not in self.__postprocInfo:
1113 self.__scanPostproc()
1114 return self.__postprocInfo["distributions"]
1115
1116 @property
1118 """Return sub-directories that contain sample-data"""
1119 if "samples" not in self.__postprocInfo:
1120 self.__scanPostproc()
1121 return self.__postprocInfo["samples"]
1122
1123 @property
1125 if "surfaces" not in self.__postprocInfo:
1126 self.__scanPostproc()
1127 return self.__postprocInfo["surfaces"]
1128
1136
1138 """Solution directory with a directory for the Chemkin-files"""
1139
1140 chemkinName = "chemkin"
1141
1142 - def __init__(self,name,archive="ArchiveDir"):
1146
1148 """@rtype: str
1149 @return: The directory with the Chemkin-Files"""
1150
1151 return path.join(self.name,self.chemkinName)
1152
1154 """Convenience class that makes sure that nothing new is created"""
1155
1156 - def __init__(self,
1157 name,
1158 region=None):
1164
1165
1166