1
2 """
3 Application class that implements pyFoamCasedReport.py
4 """
5
6 import sys,string
7 from optparse import OptionGroup
8
9 from fnmatch import fnmatch
10
11 from PyFoamApplication import PyFoamApplication
12 from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
13 from PyFoam.RunDictionary.BoundaryDict import BoundaryDict
14 from PyFoam.RunDictionary.MeshInformation import MeshInformation
15 from PyFoam.RunDictionary.ParsedParameterFile import PyFoamParserError,ParsedBoundaryDict
16
17 from PyFoam.Error import error,warning
18
19 from math import log10,ceil
20 from os import path
21
24 description="""
25 Produces human-readable reports about a case. Attention: the amount of
26 information in the reports is limited. The truth is always in the
27 dictionary-files
28 """
29
30 PyFoamApplication.__init__(self,
31 args=args,
32 description=description,
33 usage="%prog [options] <casedir>",
34 nr=1,
35 changeVersion=False,
36 interspersed=True)
37
39 report=OptionGroup(self.parser,
40 "Reports",
41 "What kind of reports should be produced")
42 self.parser.add_option_group(report)
43 select=OptionGroup(self.parser,
44 "Selection",
45 "Which data should be used for the reports")
46 self.parser.add_option_group(select)
47 internal=OptionGroup(self.parser,
48 "Internal",
49 "Details of the parser")
50 self.parser.add_option_group(internal)
51
52 report.add_option("--short-bc-report",
53 action="store_true",
54 default=False,
55 dest="shortBCreport",
56 help="Gives a short overview of the boundary-conditions in the case")
57
58 report.add_option("--long-bc-report",
59 action="store_true",
60 default=False,
61 dest="longBCreport",
62 help="Gives a full overview of the boundary-conditions in the case")
63
64 report.add_option("--dimensions",
65 action="store_true",
66 default=False,
67 dest="dimensions",
68 help="Show the dimensions of the fields")
69
70 report.add_option("--internal-field",
71 action="store_true",
72 default=False,
73 dest="internal",
74 help="Show the internal value of the fields (the initial conditions)")
75
76 select.add_option("--time",
77 action="store",
78 type="float",
79 default=None,
80 dest="time",
81 help="Time to use as the basis for the reports")
82
83 select.add_option("--region",
84 dest="region",
85 default=None,
86 help="Do the report for a special region for multi-region cases")
87
88 internal.add_option("--long-field-threshold",
89 action="store",
90 type="int",
91 default=100,
92 dest="longlist",
93 help="Fields that are longer than this won't be parsed, but read into memory (nad compared as strings)")
94
95 select.add_option("--patches",
96 action="append",
97 default=None,
98 dest="patches",
99 help="Patches which should be processed (pattern, can be used more than once)")
100
101 select.add_option("--exclude-patches",
102 action="append",
103 default=None,
104 dest="expatches",
105 help="Patches which should not be processed (pattern, can be used more than once)")
106
107 report.add_option("--processor-matrix",
108 action="store_true",
109 default=False,
110 dest="processorMatrix",
111 help="Prints the matrix how many faces from one processor interact with another")
112
113 report.add_option("--case-size",
114 action="store_true",
115 default=False,
116 dest="caseSize",
117 help="Report the number of cells, points and faces in the case")
118
119 report.add_option("--decomposition",
120 action="store_true",
121 default=False,
122 dest="decomposition",
123 help="Reports the size of the parallel decomposition")
124
126 sol=SolutionDirectory(self.parser.getArgs()[0],
127 archive=None,
128 paraviewLink=False,
129 region=self.opts.region)
130
131 needsPolyBoundaries=False
132 needsInitialTime=False
133
134 if self.opts.longBCreport:
135 needsPolyBoundaries=True
136 needsInitialTime=True
137 if self.opts.shortBCreport:
138 needsPolyBoundaries=True
139 needsInitialTime=True
140 if self.opts.dimensions:
141 needsInitialTime=True
142 if self.opts.internal:
143 needsInitialTime=True
144 if self.opts.decomposition:
145 needsPolyBoundaries=True
146
147 if needsPolyBoundaries:
148 boundary=BoundaryDict(sol.name,region=self.opts.region)
149
150 boundMaxLen=0
151 boundaryNames=[]
152 for b in boundary:
153 boundaryNames.append(b)
154
155 if self.opts.patches!=None:
156 tmp=boundaryNames
157 boundaryNames=[]
158 for b in tmp:
159 for p in self.opts.patches:
160 if fnmatch(b,p):
161 boundaryNames.append(b)
162 break
163
164 if self.opts.expatches!=None:
165 tmp=boundaryNames
166 boundaryNames=[]
167 for b in tmp:
168 keep=True
169 for p in self.opts.expatches:
170 if fnmatch(b,p):
171 keep=False
172 break
173 if keep:
174 boundaryNames.append(b)
175
176 for b in boundaryNames:
177 boundMaxLen=max(boundMaxLen,len(b))
178 boundaryNames.sort()
179
180 if self.opts.time==None:
181 procTime="constant"
182 else:
183 try:
184 procTime=sol.timeName(sol.timeIndex(self.opts.time,minTime=True))
185 except IndexError:
186 error("The specified time",self.opts.time,"doesn't exist in the case")
187
188 if needsInitialTime:
189 fields={}
190
191 if self.opts.time==None:
192 try:
193 time=sol.timeName(0)
194 except IndexError:
195 error("There is no timestep in the case")
196 else:
197 try:
198 time=sol.timeName(sol.timeIndex(self.opts.time,minTime=True))
199 except IndexError:
200 error("The specified time",self.opts.time,"doesn't exist in the case")
201
202
203 tDir=sol[time]
204
205 nameMaxLen=0
206
207 for f in tDir:
208 try:
209 fields[f.baseName()]=f.getContent(listLengthUnparsed=self.opts.longlist)
210 nameMaxLen=max(nameMaxLen,len(f.baseName()))
211 except PyFoamParserError,e:
212 warning("Couldn't parse",f.name,"because of an error:",e," -> skipping")
213
214 fieldNames=fields.keys()
215 fieldNames.sort()
216
217 if self.opts.caseSize:
218 print "Size of the case"
219 print
220 info=MeshInformation(sol.name)
221 print "Faces: \t",info.nrOfFaces()
222 print "Points: \t",info.nrOfPoints()
223 try:
224 print "Cells: \t",info.nrOfCells()
225 except:
226 print "Not available"
227
228 if self.opts.decomposition:
229 if sol.nrProcs()<2:
230 error("The case is not decomposed")
231 print "Case is decomposed for",sol.nrProcs(),"processors"
232
233 nCells=[]
234 nFaces=[]
235 nPoints=[]
236 for p in sol.processorDirs():
237 info=MeshInformation(sol.name,processor=p)
238 nPoints.append(info.nrOfPoints())
239 nFaces.append(info.nrOfFaces())
240 nCells.append(info.nrOfCells())
241
242 digits=int(ceil(log10(max(sol.nrProcs(),
243 max(nCells),
244 max(nFaces),
245 max(nPoints)
246 ))))+2
247 nameLen=max(len("Points"),boundMaxLen)
248
249 nrFormat ="%%%dd" % digits
250 nameFormat="%%%ds" % nameLen
251
252 print " "*nameLen,"|",
253 for i in range(sol.nrProcs()):
254 print nrFormat % i,
255 print
256
257 print "-"*(nameLen+3+(digits+1)*sol.nrProcs())
258
259 print nameFormat % "Points","|",
260 for p in nPoints:
261 print nrFormat % p,
262 print
263 print nameFormat % "Faces","|",
264 for p in nFaces:
265 print nrFormat % p,
266 print
267 print nameFormat % "Cells","|",
268 for p in nCells:
269 print nrFormat % p,
270 print
271
272 print "-"*(nameLen+3+(digits+1)*sol.nrProcs())
273
274 for b in boundaryNames:
275 print nameFormat % b,"|",
276 for p in sol.processorDirs():
277 print nrFormat % ParsedBoundaryDict(sol.boundaryDict(processor=p))[b]["nFaces"],
278 print
279
280 if self.opts.longBCreport:
281 print "\nThe boundary conditions for t =",time
282
283 for b in boundaryNames:
284 print "\nBoundary: \t",b
285 bound=boundary[b]
286 print " type:\t",bound["type"],
287 if "physicalType" in bound:
288 print "( Physical:",bound["physicalType"],")",
289 print " \t Faces:",bound["nFaces"]
290 for fName in fieldNames:
291 print " ",fName,
292 f=fields[fName]
293 if b not in f["boundaryField"]:
294 print " "*(nameMaxLen-len(fName)+2)+": MISSING !!!"
295 else:
296 bf=f["boundaryField"][b]
297 maxKeyLen=0
298 for k in bf:
299 maxKeyLen=max(maxKeyLen,len(k))
300
301 print " "*(nameMaxLen-len(fName)+2)+"type "+" "*(maxKeyLen-4)+": ",bf["type"]
302 for k in bf:
303 if k!="type":
304 print " "*(nameMaxLen+6),k," "*(maxKeyLen-len(k))+": ",
305 cont=str(bf[k])
306 if cont.find("\n")>=0:
307 print cont[:cont.find("\n")],"..."
308 else:
309 print cont
310
311 if self.opts.shortBCreport:
312 print "\nTable of boundary conditions for t =",time
313 print
314
315 colLen = {}
316 types={}
317 hasPhysical=False
318 nameMaxLen=max(nameMaxLen,len("Patch Type"))
319 for b in boundary:
320 colLen[b]=max(len(b),len(boundary[b]["type"]))
321 colLen[b]=max(len(b),len(str(boundary[b]["nFaces"])))
322 if "physicalType" in boundary[b]:
323 hasPhysical=True
324 nameMaxLen=max(nameMaxLen,len("Physical Type"))
325 colLen[b]=max(colLen[b],len(boundary[b]["physicalType"]))
326
327 types[b]={}
328
329 for fName in fields:
330 f=fields[fName]
331 if b not in f["boundaryField"]:
332 types[b][fName]="MISSING"
333 else:
334 types[b][fName]=f["boundaryField"][b]["type"]
335 colLen[b]=max(colLen[b],len(types[b][fName]))
336
337 print " "*(nameMaxLen),
338 nr=nameMaxLen+1
339 for b in boundaryNames:
340 print "| "+b+" "*(colLen[b]-len(b)),
341 nr+=colLen[b]+3
342 print
343 print "-"*nr
344 print "Patch Type"+" "*(nameMaxLen-len("Patch Type")),
345 for b in boundaryNames:
346 t=boundary[b]["type"]
347 print "| "+t+" "*(colLen[b]-len(t)),
348 print
349 if hasPhysical:
350 print "Physical Type"+" "*(nameMaxLen-len("Physical Type")),
351 for b in boundaryNames:
352 t=""
353 if "physicalType" in boundary[b]:
354 t=boundary[b]["physicalType"]
355 print "| "+t+" "*(colLen[b]-len(t)),
356 print
357 print "Length"+" "*(nameMaxLen-len("Length")),
358 for b in boundaryNames:
359 s=str(boundary[b]["nFaces"])
360 print "| "+s+" "*(colLen[b]-len(s)),
361 print
362 print "-"*nr
363 for fName in fieldNames:
364 print fName+" "*(nameMaxLen-len(fName)),
365 for b in boundaryNames:
366 t=types[b][fName]
367 print "| "+t+" "*(colLen[b]-len(t)),
368 print
369
370 print
371
372 if self.opts.dimensions:
373 print "\nDimensions of fields for t =",time
374 print
375
376 head="Name"+" "*(nameMaxLen-len("Name"))+" : [kg m s K mol A cd]"
377 print head
378 print "-"*len(head)
379 for fName in fieldNames:
380 f=fields[fName]
381
382 print fName+" "*(nameMaxLen-len(fName))+" :",f["dimensions"]
383
384 if self.opts.internal:
385 print "\Internal value of fields for t =",time
386 print
387
388 head="Name"+" "*(nameMaxLen-len("Name"))+" : Value "
389 print head
390 print "-"*len(head)
391 for fName in fieldNames:
392 f=fields[fName]
393
394 print fName+" "*(nameMaxLen-len(fName))+" :",
395
396 cont=str(f["internalField"])
397 if cont.find("\n")>=0:
398 print cont[:cont.find("\n")],"..."
399 else:
400 print cont
401
402 if self.opts.processorMatrix:
403 if sol.nrProcs()<2:
404 error("The case is not decomposed")
405
406 matrix=[ [0,]*sol.nrProcs() for i in range(sol.nrProcs())]
407
408 for i,p in enumerate(sol.processorDirs()):
409 bound=ParsedBoundaryDict(path.join(sol.name,p,procTime,"polyMesh","boundary"))
410 for j in range(sol.nrProcs()):
411 name="procBoundary%dto%d" %(i,j)
412 if name in bound:
413 matrix[i][j]=bound[name]["nFaces"]
414
415 print "Matrix of processor interactions (faces)"
416 print
417
418 digits=int(ceil(log10(sol.nrProcs())))+2
419 colDigits=int(ceil(log10(max(digits,max(max(matrix))))))+2
420
421 format="%%%dd" % digits
422 colFormat="%%%dd" % colDigits
423
424 print " "*(digits),"|",
425 for j in range(sol.nrProcs()):
426 print colFormat % j,
427 print
428 print "-"*(digits+3+(colDigits+1)*sol.nrProcs())
429
430 for i,col in enumerate(matrix):
431 print format % i,"|",
432 for j,nr in enumerate(col):
433 print colFormat % matrix[i][j],
434 print
435