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
7
8 from TimeDirectory import TimeDirectory
9
10 from os import listdir,path,mkdir,symlink,stat
11 from stat import ST_CTIME
12 import tarfile,fnmatch
13 import re
14
16 """Represents a solution directory
17
18 In the solution directory subdirectories whose names are numbers
19 are assumed to be solutions for a specific time-step
20
21 A sub-directory (called the Archive) is created to which solution
22 data is copied"""
23
24 - def __init__(self,name,archive="ArchiveDir",paraviewLink=True,region=None):
25 """@param name: Name of the solution directory
26 @param archive: name of the directory where the lastToArchive-method
27 should copy files, if None no archive is created
28 @param paraviewLink: Create a symbolic link controlDict.foam for paraview
29 @param region: Mesh region for multi-region cases"""
30
31 self.name=path.abspath(name)
32 self.archive=None
33 if archive!=None:
34 self.archive=path.join(name,archive)
35 if not path.exists(self.archive):
36 mkdir(self.archive)
37
38 self.region=region
39 self.backups=[]
40
41 self.lastReread=0L
42 self.reread()
43
44 self.essential=[self.systemDir(),self.constantDir(),self.initialDir()]
45
46 if paraviewLink and not path.exists(self.controlDict()+".foam"):
47 symlink(path.basename(self.controlDict()),self.controlDict()+".foam")
48
52
54 self.reread()
55
56 if self.timeName(item)!=None:
57 return True
58 else:
59 return False
60
69
71 self.reread()
72 if type(key)!=str:
73 raise TypeError(type(key),"of",key,"is not 'str'")
74
75 if type(value)!=TimeDirectory:
76 raise TypeError(type(value),"is not TimeDirectory")
77
78 dest=TimeDirectory(self.name,key,create=True,region=self.region)
79 dest.copy(value)
80
81 self.reread(force=True)
82
84 self.reread()
85 nm=self.timeName(key)
86 if nm==None:
87 raise KeyError(key)
88
89 self.execute("rm -rf "+path.join(self.name,nm))
90
91 self.reread(force=True)
92
97
99 """Finds the name of a directory that corresponds with the given parameter
100 @param item: the time that should be found
101 @param minTime: search for the time with the minimal difference.
102 Otherwise an exact match will be searched"""
103
104 if type(item)==int:
105 return self.times[item]
106 else:
107 ind=self.timeIndex(item,minTime)
108 if ind==None:
109 return None
110 else:
111 return self.times[ind]
112
114 """Finds the index of a directory that corresponds with the given parameter
115 @param item: the time that should be found
116 @param minTime: search for the time with the minimal difference.
117 Otherwise an exact match will be searched"""
118 self.reread()
119
120 time=float(item)
121 result=None
122
123 if minTime:
124 result=0
125 for i in range(1,len(self.times)):
126 if abs(float(self.times[result])-time)>abs(float(self.times[i])-time):
127 result=i
128 else:
129 for i in range(len(self.times)):
130 t=self.times[i]
131 if abs(float(t)-time)<1e-6:
132 if result==None:
133 result=i
134 elif abs(float(t)-time)<abs(float(self.times[result])-time):
135 result=i
136
137 return result
138
140 """Checks whether this is a valid case directory by looking for
141 the system- and constant-directories and the controlDict-file"""
142
143 return len(self.missingFiles())==0
144
161
163 """add directory to the list that is needed to clone this case
164 @param name: name of the subdirectory (the case directory is prepended)"""
165 self.essential.append(path.join(self.name,name))
166
167 - def cloneCase(self,name,svnRemove=True,followSymlinks=False):
168 """create a clone of this case directory. Remove the target directory, if it already exists
169
170 @param name: Name of the new case directory
171 @param svnRemove: Look for .svn-directories and remove them
172 @param followSymlinks: Follow symbolic links instead of just copying them
173 @rtype: L{SolutionDirectory} or correct subclass
174 @return: The target directory"""
175
176 cpOptions="-R"
177 if followSymlinks:
178 cpOptions+=" -L"
179
180 if path.exists(name):
181 self.execute("rm -r "+name)
182 mkdir(name)
183 for d in self.essential:
184 self.execute("cp "+cpOptions+" "+d+" "+name)
185
186 if svnRemove:
187 self.execute("find "+name+" -name .svn -exec rm -rf {} \\; -prune")
188
189 return self.__class__(name,archive=self.archive)
190
191 - def packCase(self,tarname,last=False,exclude=[],additional=[],base=None):
192 """Packs all the important files into a compressed tarfile.
193 Uses the essential-list and excludes the .svn-directories.
194 Also excludes files ending with ~
195 @param tarname: the name of the tar-file
196 @param last: add the last directory to the list of directories to be added
197 @param exclude: List with additional glob filename-patterns to be excluded
198 @param additional: List with additional glob filename-patterns
199 that are to be added
200 @param base: Different name that is to be used as the baseName for the case inside the tar"""
201
202 ex=["*~",".svn"]+exclude
203 members=self.essential[:]
204 if last:
205 if self.getLast()!=self.first:
206 members.append(self.latestDir())
207 for p in additional:
208 for f in listdir(self.name):
209 if (f not in members) and fnmatch.fnmatch(f,p):
210 members.append(path.join(self.name,f))
211
212 tar=tarfile.open(tarname,"w:gz")
213
214 for m in members:
215 self.addToTar(tar,m,exclude=ex,base=base)
216
217 tar.close()
218
219 - def addToTar(self,tar,name,exclude=[],base=None):
220 """The workhorse for the packCase-method"""
221
222 if base==None:
223 base=path.basename(self.name)
224
225 for e in exclude:
226 if fnmatch.fnmatch(path.basename(name),e):
227 return
228
229 if path.isdir(name):
230 for m in listdir(name):
231 self.addToTar(tar,path.join(name,m),exclude=exclude,base=base)
232 else:
233 arcname=path.join(base,name[len(self.name)+1:])
234 tar.add(name,arcname=arcname)
235
237 """Get a list of the times in the processor0-directory"""
238 result=[]
239
240 proc0=path.join(self.name,"processor0")
241 if path.exists(proc0):
242 for f in listdir(proc0):
243 try:
244 val=float(f)
245 result.append(f)
246 except ValueError:
247 pass
248 result.sort(self.sorttimes)
249 return result
250
251 - def reread(self,force=False):
252 """Rescan the directory for the time directories"""
253
254 if not force and stat(self.name)[ST_CTIME]<=self.lastReread:
255 return
256
257 self.times=[]
258 self.first=None
259 self.last=None
260 self.procDirs=0
261
262 for f in listdir(self.name):
263 try:
264 val=float(f)
265 self.times.append(f)
266 if self.first==None:
267 self.first=f
268 else:
269 if float(f)<float(self.first):
270 self.first=f
271 if self.last==None:
272 self.last=f
273 else:
274 if float(f)>float(self.last):
275 self.last=f
276
277 except ValueError:
278 if re.compile("processor[0-9]+").match(f):
279 self.procDirs+=1
280
281 self.lastReread=stat(self.name)[ST_CTIME]
282
283 self.times.sort(self.sorttimes)
284
286 """List with the processor directories"""
287 dirs=[]
288 for f in listdir(self.name):
289 if re.compile("processor[0-9]+").match(f):
290 dirs.append(f)
291
292 return dirs
293
295 """The number of directories with processor-data"""
296 self.reread()
297 return self.procDirs
298
300 """Sort function for the solution files"""
301 if(float(x)==float(y)):
302 return 0
303 elif float(x)<float(y):
304 return -1
305 else:
306 return 1
307
309 """ @return: List of all the available times"""
310 self.reread()
311 return self.times
312
314 """add file to list of files that are to be copied to the
315 archive"""
316 self.backups.append(path.join(self.name,pth))
317
319 """@return: the last time for which a solution exists
320 @rtype: str"""
321 self.reread()
322 return self.last
323
325 """copy the last solution (plus the backup-files to the
326 archive)
327
328 @param name: name of the sub-directory in the archive"""
329 if self.archive==None:
330 print "Warning: nor Archive-directory"
331 return
332
333 self.reread()
334 fname=path.join(self.archive,name)
335 if path.exists(fname):
336 self.execute("rm -r "+fname)
337 mkdir(fname)
338 self.execute("cp -r "+path.join(self.name,self.last)+" "+fname)
339 for f in self.backups:
340 self.execute("cp -r "+f+" "+fname)
341
342 - def clearResults(self,after=None,removeProcs=False,keepLast=False,vtk=True,keepRegular=False):
343 """remove all time-directories after a certain time. If not time ist
344 set the initial time is used
345 @param after: time after which directories ar to be removed
346 @param removeProcs: if True the processorX-directories are removed.
347 Otherwise the timesteps after last are removed from the
348 processor-directories
349 @param keepLast: Keep the data from the last timestep
350 @param vtk: Remove the VTK-directory if it exists
351 @param keepRegular: keep all the times (only remove processor and other stuff)"""
352
353 self.reread()
354
355 last=self.getLast()
356
357 if after==None:
358 try:
359 time=float(self.first)
360 except TypeError:
361 warning("The first timestep in",self.name," is ",self.first,"not a number. Doing nothing")
362 return
363 else:
364 time=float(after)
365
366 if not keepRegular:
367 for f in self.times:
368 if float(f)>time and not (keepLast and f==last):
369 self.execute("rm -r "+path.join(self.name,f))
370
371 if path.exists(path.join(self.name,"VTK")) and vtk:
372 self.execute("rm -r "+path.join(self.name,"VTK"))
373
374 if self.nrProcs():
375 for f in listdir(self.name):
376 if re.compile("processor[0-9]+").match(f):
377 if removeProcs:
378 self.execute("rm -r "+path.join(self.name,f))
379 else:
380 pDir=path.join(self.name,f)
381 for t in listdir(pDir):
382 try:
383 val=float(t)
384 if val>time:
385 self.execute("rm -r "+path.join(pDir,t))
386 except ValueError:
387 pass
388
390 """Clear all files that fit a certain shell (glob) pattern
391 @param glob: the pattern which the files are going to fit"""
392
393 self.execute("rm -rf "+path.join(self.name,glob))
394
396 """Remove additional directories
397 @param pyfoam: rremove all directories typically created by PyFoam"""
398
399 if pyfoam:
400 self.clearPattern("PyFoam.?*")
401 self.clearPattern("*?.analyzed")
402
403 - def clear(self,after=None,processor=True,pyfoam=True,keepLast=False,vtk=True,keepRegular=False):
404 """One-stop-shop to remove data
405 @param after: time after which directories ar to be removed
406 @param processor: remove the processorXX directories
407 @param pyfoam: rremove all directories typically created by PyFoam
408 @param keepLast: Keep the last time-step"""
409 self.clearResults(after=after,removeProcs=processor,keepLast=keepLast,vtk=vtk,keepRegular=keepRegular)
410 self.clearOther(pyfoam=pyfoam)
411
413 """@return: the name of the first time-directory (==initial
414 conditions
415 @rtype: str"""
416 self.reread()
417
418 if self.first:
419 return path.join(self.name,self.first)
420 else:
421 return None
422
424 """@return: the name of the first last-directory (==simulation
425 results)
426 @rtype: str"""
427 self.reread()
428
429 last=self.getLast()
430 if last:
431 return path.join(self.name,last)
432 else:
433 return None
434
436 """@param region: Specify the region for cases with more than 1 mesh
437 @param processor: name of the processor directory
438 @return: the name of the C{constant}-directory
439 @rtype: str"""
440 pre=self.name
441 if processor!=None:
442 pre=path.join(pre,processor)
443
444 if region==None and self.region!=None:
445 region=self.region
446 if region:
447 return path.join(pre,"constant",region)
448 else:
449 return path.join(pre,"constant")
450
452 """@param region: Specify the region for cases with more than 1 mesh
453 @return: the name of the C{system}-directory
454 @rtype: str"""
455 if region==None and self.region!=None:
456 region=self.region
457 if region:
458 return path.join(self.name,"system",region)
459 else:
460 return path.join(self.name,"system")
461
463 """@return: the name of the C{controlDict}
464 @rtype: str"""
465 return path.join(self.systemDir(),"controlDict")
466
467 - def polyMeshDir(self,region=None,time="constant",processor=None):
468 """@param region: Specify the region for cases with more than 1 mesh
469 @return: the name of the C{polyMesh}
470 @param time: Time for which the mesh should be looked at
471 @param processor: Name of the processor directory for decomposed cases
472 @rtype: str"""
473 if region==None and self.region!=None:
474 region=self.region
475 return path.join(self.constantDir(region=region,processor=processor),"polyMesh")
476
477 - def boundaryDict(self,region=None,time="constant",processor=None):
478 """@param region: Specify the region for cases with more than 1 mesh
479 @return: name of the C{boundary}-file
480 @rtype: str"""
481 if region==None and self.region!=None:
482 region=self.region
483 return path.join(self.polyMeshDir(region=region,time=time,processor=processor),"boundary")
484
486 """@param region: Specify the region for cases with more than 1 mesh
487 @return: the name of the C{blockMeshDict} if it exists. Returns
488 an empty string if it doesn't
489 @rtype: str"""
490 if region==None and self.region!=None:
491 region=self.region
492 p=path.join(self.polyMeshDir(region=region),"blockMeshDict")
493 if path.exists(p):
494 return p
495 else:
496 return ""
497
499 """create a file in the solution directory and return a
500 corresponding BasicFile-object
501
502 @param name: Name of the file
503 @rtype: L{BasicFile}"""
504 return BasicFile(path.join(self.name,name))
505
507 """Gets a list of all the available mesh regions by checking all
508 directories in constant and using all those that have a polyMesh-subdirectory"""
509 lst=[]
510 for d in self.listDirectory(self.constantDir()):
511 if path.isdir(path.join(self.constantDir(),d)):
512 if path.exists(self.polyMeshDir(region=d)):
513 lst.append(d)
514 lst.sort()
515 return lst
516
518 """Solution directory with a directory for the Chemkin-files"""
519
520 chemkinName = "chemkin"
521
522 - def __init__(self,name,archive="ArchiveDir"):
526
528 """@rtype: str
529 @return: The directory with the Chemkin-Files"""
530
531 return path.join(self.name,self.chemkinName)
532