1 """
2 Application-class that implements pyFoamListCases.py
3 """
4 from optparse import OptionGroup
5 from os import path,listdir,stat
6 from glob import glob
7 from PyFoam.ThirdParty.six.moves import cPickle as pickle
8 from PyFoam.ThirdParty.six import string_types
9 import time,datetime
10 from stat import ST_MTIME
11 import re
12 import os
13
14 from .PyFoamApplication import PyFoamApplication
15
16 from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
17 from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile,PyFoamParserError
18
19 from PyFoam import configuration
20
21 from PyFoam.ThirdParty.six import print_,iteritems,PY3
22
23 from PyFoam.Basics.Utilities import humanReadableSize,diskUsage
24
25 if PY3:
26 long=int
27
29 - def __init__(self,
30 args=None,
31 **kwargs):
32 description="""\
33 List the valid OpenFOAM-cases in a number of directories along with
34 some basic information (number of timesteps, last timestep,
35 etc). Currently doesn't honor the parallel data
36 """
37 PyFoamApplication.__init__(self,
38 args=args,
39 description=description,
40 usage="%prog [<directories>]",
41 interspersed=True,
42 changeVersion=False,
43 nr=0,
44 exactNr=False,
45 **kwargs)
46
47 sortChoices=["name","first","last","mtime","nrSteps","procs","diskusage","pFirst","pLast","nrParallel","nowTime","state","lastOutput","startedAt"]
48
50 what=OptionGroup(self.parser,
51 "What",
52 "Define what should be shown")
53 self.parser.add_option_group(what)
54
55 what.add_option("--dump",
56 action="store_true",
57 dest="dump",
58 default=False,
59 help="Dump the information as Python-dictionaries")
60
61 what.add_option("--disk-usage",
62 action="store_true",
63 dest="diskusage",
64 default=False,
65 help="Show the disk-usage of the case (in MB) - may take a long time")
66
67 what.add_option("--parallel-info",
68 action="store_true",
69 dest="parallel",
70 default=False,
71 help="Print information about parallel runs (if present): number of processors and processor first and last time. The mtime will be that of the processor-directories")
72
73 what.add_option("--no-state",
74 action="store_false",
75 dest="state",
76 default=True,
77 help="Don't read state-files")
78
79 what.add_option("--advanced-state",
80 action="store_true",
81 dest="advancedState",
82 default=False,
83 help="Additional state information (run started, last output seen)")
84
85 what.add_option("--estimate-end-time",
86 action="store_true",
87 dest="estimateEndTime",
88 default=False,
89 help="Print an estimated end time (calculated from the start time of the run, the current time and the current simulation time)")
90
91 what.add_option("--start-end-time",
92 action="store_true",
93 dest="startEndTime",
94 default=False,
95 help="Start and end time from the controlDict")
96
97 what.add_option("--custom-data",
98 action="append",
99 dest="customData",
100 default=[],
101 help="Specification of additional data that is read from the pickled data-sets. The format is 'name=spec1::spec2::...' where 'name' is the name under which the data ist shown and 'specN' are the dictionary keys under which the data is accessed. If only 'spec1::spec2::..' is given then a name of the form 'CustomN' will be used. Can be specified more than once")
102
103 what.add_option("--solver-name-for-custom-data",
104 action="store",
105 dest="solverNameForCustom",
106 default=None,
107 help="This is used if '--custom-data' is specified as the data will be searched in 'PyFoamRunner.<solver name>.analyzed'. If unset then the utility will try to automatically determine the name of the solver which might be wrong")
108
109 how=OptionGroup(self.parser,
110 "How",
111 "How the things should be shown")
112 self.parser.add_option_group(how)
113
114 how.add_option("--sort-by",
115 type="choice",
116 action="store",
117 dest="sort",
118 default=configuration().get("CommandOptionDefaults","sortListCases",default="name"),
119 choices=self.sortChoices,
120 help="Sort the cases by a specific key (Keys: "+", ".join(self.sortChoices)+") Default: %default")
121 how.add_option("--reverse-sort",
122 action="store_true",
123 dest="reverse",
124 default=False,
125 help="Sort in reverse order")
126 how.add_option("--relative-times",
127 action="store_true",
128 dest="relativeTime",
129 default=False,
130 help="Show the timestamps relative to the current time")
131
132 behave=OptionGroup(self.parser,
133 "Behaviour",
134 "Additional output etc")
135 self.parser.add_option_group(behave)
136
137 behave.add_option("--progress",
138 action="store_true",
139 dest="progress",
140 default=False,
141 help="Print the directories while they are being processed")
142
150
152 dirs=self.parser.getArgs()
153
154 if len(dirs)==0:
155 dirs=[path.curdir]
156
157 cData=[]
158 totalDiskusage=0
159 useSolverInData=False
160
161 self.hasState=False
162
163 customData=[]
164 for i,c in enumerate(self.opts.customData):
165 lst=c.split("=")
166 if len(lst)==2:
167 name,spec=lst
168 name+="_"
169 elif len(lst)==1:
170 name,spec="Custom%d" % (i+1),c
171 else:
172 self.error("Custom specification",c,"does not fit the pattern 'name=subs1::subs2::..'")
173 customData.append((name,spec.split("::")))
174
175 if len(customData)>0 and not self.opts.solverNameForCustom:
176 self.warning("Parameter '--solver-name-for-custom-data' should be set if '--custom-data' is used")
177 useSolverInData=True
178
179 for d in dirs:
180 for n in listdir(d):
181 cName=path.join(d,n)
182 if path.isdir(cName):
183 try:
184 sol=SolutionDirectory(cName,archive=None,paraviewLink=False)
185 if sol.isValid():
186 if self.opts.progress:
187 print_("Processing",cName)
188
189 data={}
190
191 data["mtime"]=stat(cName)[ST_MTIME]
192 times=sol.getTimes()
193 try:
194 data["first"]=times[0]
195 except IndexError:
196 data["first"]="None"
197 try:
198 data["last"]=times[-1]
199 except IndexError:
200 data["last"]="None"
201 data["nrSteps"]=len(times)
202 data["procs"]=sol.nrProcs()
203 data["pFirst"]=-1
204 data["pLast"]=-1
205 data["nrParallel"]=-1
206 if self.opts.parallel:
207 pTimes=sol.getParallelTimes()
208 data["nrParallel"]=len(pTimes)
209 if len(pTimes)>0:
210 data["pFirst"]=pTimes[0]
211 data["pLast"]=pTimes[-1]
212 data["name"]=cName
213 data["diskusage"]=-1
214 if self.opts.diskusage:
215 data["diskusage"]=diskUsage(cName)
216 totalDiskusage+=data["diskusage"]
217 if self.opts.parallel:
218 for f in listdir(cName):
219 if re.compile("processor[0-9]+").match(f):
220 data["mtime"]=max(stat(path.join(cName,f))[ST_MTIME],data["mtime"])
221
222 if self.opts.state:
223 try:
224 data["nowTime"]=float(self.readState(sol,"CurrentTime"))
225 except ValueError:
226 data["nowTime"]=None
227
228 try:
229 data["lastOutput"]=time.mktime(time.strptime(self.readState(sol,"LastOutputSeen")))
230 except ValueError:
231 data["lastOutput"]="nix"
232
233 data["state"]=self.readState(sol,"TheState")
234
235 if self.opts.state or self.opts.estimateEndTime:
236 try:
237 data["startedAt"]=time.mktime(time.strptime(self.readState(sol,"StartedAt")))
238 except ValueError:
239 data["startedAt"]="nix"
240
241 if self.opts.startEndTime or self.opts.estimateEndTime:
242 try:
243 ctrlDict=ParsedParameterFile(sol.controlDict(),doMacroExpansion=True)
244 except PyFoamParserError:
245
246 try:
247 ctrlDict=ParsedParameterFile(sol.controlDict())
248 except PyFoamParserError:
249 ctrlDict=None
250 if ctrlDict:
251 data["startTime"]=ctrlDict["startTime"]
252 data["endTime"]=ctrlDict["endTime"]
253 else:
254 data["startTime"]=None
255 data["endTime"]=None
256
257 if self.opts.estimateEndTime:
258 data["endTimeEstimate"]=None
259 if self.readState(sol,"TheState")=="Running":
260 gone=time.time()-data["startedAt"]
261 try:
262 current=float(self.readState(sol,"CurrentTime"))
263 frac=(current-data["startTime"])/(data["endTime"]-data["startTime"])
264 except ValueError:
265 frac=0
266 if frac>0:
267 data["endTimeEstimate"]=data["startedAt"]+gone/frac
268
269 if len(customData)>0:
270 fn=None
271 pickleFile=None
272 if useSolverInData:
273 data["solver"]="none found"
274
275 for f in ["pickledData","pickledUnfinishedData","pickledStartData"]:
276 dirAndTime=[]
277 for g in glob(path.join(cName,"*.analyzed")):
278 pName=path.join(g,f)
279 base=path.basename(g)
280 if base.find("PyFoamRunner.")==0:
281 solverName=base[len("PyFoamRunner."):-len(".analyzed")]
282 else:
283 solverName=None
284 if path.exists(pName):
285 dirAndTime.append((path.getmtime(pName),solverName,pName))
286 dirAndTime.sort(key=lambda x:x[0])
287 if len(dirAndTime)>0:
288 data["solver"]=dirAndTime[-1][1]
289 pickleFile=dirAndTime[-1][2]
290 break
291
292 solverName=data["solver"]
293 else:
294 solverName=self.opts.solverNameForCustom
295
296 if pickleFile:
297 fn=pickleFile
298 else:
299 for f in ["pickledData","pickledUnfinishedData","pickledStartData"]:
300 fp=path.join(cName,"PyFoamRunner."+solverName+".analyzed",f)
301 if path.exists(fp):
302 fn=fp
303 break
304 if fn:
305 raw=pickle.Unpickler(open(fn)).load()
306 for n,spec in customData:
307 dt=raw
308 for k in spec:
309 try:
310 dt=dt[k]
311 except KeyError:
312 dt="No key '"+k+"'"
313 break
314 if isinstance(dt,string_types):
315 break
316 data[n]=dt
317 else:
318 for n,spec in customData:
319 data[n]="no file"
320
321 cData.append(data)
322 except OSError:
323 print_(cName,"is unreadable")
324
325 if self.opts.progress:
326 print_("Sorting data")
327
328
329 cData.sort(key=lambda x:x[self.opts.sort],reverse=self.opts.reverse)
330
331 if len(cData)==0:
332 print_("No cases found")
333 return
334
335 if self.opts.dump:
336 print_(cData)
337 return
338
339 lens={}
340 for k in list(cData[0].keys()):
341 lens[k]=len(k)
342 for c in cData:
343 for k in ["mtime","lastOutput","startedAt","endTimeEstimate"]:
344 try:
345 if c[k]!=None:
346 if self.opts.relativeTime:
347 c[k]=datetime.timedelta(seconds=long(time.time()-c[k]))
348 else:
349 c[k]=time.asctime(time.localtime(c[k]))
350 except KeyError:
351 pass
352 except TypeError:
353 c[k]=None
354
355 try:
356 c["diskusage"]=humanReadableSize(c["diskusage"])
357 except KeyError:
358 pass
359
360 for k,v in iteritems(c):
361 lens[k]=max(lens[k],len(str(v)))
362
363 format=""
364 spec=["mtime"," | ","first"," - ","last"," (","nrSteps",") "]
365 if self.opts.parallel:
366 spec+=["| ","procs"," : ","pFirst"," - ","pLast"," (","nrParallel",") | "]
367 if self.opts.diskusage:
368 spec+=["diskusage"," | "]
369 if self.hasState:
370 spec+=["nowTime"," s ","state"," | "]
371 if self.opts.advancedState:
372 spec+=["lastOutput"," | ","startedAt"," | "]
373 if self.opts.estimateEndTime:
374 if not self.opts.advancedState:
375 spec+=["startedAt"," | "]
376 spec+=["endTimeEstimate"," | "]
377 if self.opts.startEndTime:
378 spec+=["startTime"," | ","endTime"," | "]
379
380 if useSolverInData:
381 spec+=["solver"," | "]
382 for n,s in customData:
383 spec+=[n," | "]
384
385 spec+=["name"]
386
387 for i,l in enumerate(spec):
388 if not l in list(cData[0].keys()):
389 format+=l
390 else:
391 if i<len(spec)-1:
392 format+="%%(%s)%ds" % (l,lens[l])
393 else:
394 format+="%%(%s)s" % (l)
395
396 if self.opts.progress:
397 print_("Printing\n\n")
398
399 header=format % dict(list(zip(list(cData[0].keys()),list(cData[0].keys()))))
400 print_(header)
401 print_("-"*len(header))
402
403 for d in cData:
404 for k in list(d.keys()):
405 d[k]=str(d[k])
406 print_(format % d)
407
408 if self.opts.diskusage:
409 print_("Total disk-usage:",humanReadableSize(totalDiskusage))
410
411
412
413