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