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],archive=None,paraviewLink=False,region=self.opts.region)
127
128 needsPolyBoundaries=False
129 needsInitialTime=False
130
131 if self.opts.longBCreport:
132 needsPolyBoundaries=True
133 needsInitialTime=True
134 if self.opts.shortBCreport:
135 needsPolyBoundaries=True
136 needsInitialTime=True
137 if self.opts.dimensions:
138 needsInitialTime=True
139 if self.opts.internal:
140 needsInitialTime=True
141 if self.opts.decomposition:
142 needsPolyBoundaries=True
143
144 if needsPolyBoundaries:
145 boundary=BoundaryDict(sol.name,region=self.opts.region)
146
147 boundMaxLen=0
148 boundaryNames=[]
149 for b in boundary:
150 boundaryNames.append(b)
151
152 if self.opts.patches!=None:
153 tmp=boundaryNames
154 boundaryNames=[]
155 for b in tmp:
156 for p in self.opts.patches:
157 if fnmatch(b,p):
158 boundaryNames.append(b)
159 break
160
161 if self.opts.expatches!=None:
162 tmp=boundaryNames
163 boundaryNames=[]
164 for b in tmp:
165 keep=True
166 for p in self.opts.expatches:
167 if fnmatch(b,p):
168 keep=False
169 break
170 if keep:
171 boundaryNames.append(b)
172
173 for b in boundaryNames:
174 boundMaxLen=max(boundMaxLen,len(b))
175 boundaryNames.sort()
176
177 if self.opts.time==None:
178 procTime="constant"
179 else:
180 procTime=sol.timeName(sol.timeIndex(self.opts.time,minTime=True))
181
182 if needsInitialTime:
183 fields={}
184
185 if self.opts.time==None:
186 time=sol.timeName(0)
187 else:
188 time=sol.timeName(sol.timeIndex(self.opts.time,minTime=True))
189
190
191
192 tDir=sol[time]
193
194 nameMaxLen=0
195
196 for f in tDir:
197 try:
198 fields[f.baseName()]=f.getContent(listLengthUnparsed=self.opts.longlist)
199 nameMaxLen=max(nameMaxLen,len(f.baseName()))
200 except PyFoamParserError,e:
201 warning("Couldn't parse",f.name,"because of an error:",e," -> skipping")
202
203 fieldNames=fields.keys()
204 fieldNames.sort()
205
206 if self.opts.caseSize:
207 print "Size of the case"
208 print
209 info=MeshInformation(sol.name)
210 print "Faces: \t",info.nrOfFaces()
211 print "Points: \t",info.nrOfPoints()
212 try:
213 print "Cells: \t",info.nrOfCells()
214 except:
215 print "Not available"
216
217 if self.opts.decomposition:
218 if sol.nrProcs()<2:
219 error("The case is not decomposed")
220 print "Case is decomposed for",sol.nrProcs(),"processors"
221
222 nCells=[]
223 nFaces=[]
224 nPoints=[]
225 for p in sol.processorDirs():
226 info=MeshInformation(sol.name,processor=p)
227 nPoints.append(info.nrOfPoints())
228 nFaces.append(info.nrOfFaces())
229 nCells.append(info.nrOfCells())
230
231 digits=int(ceil(log10(max(sol.nrProcs(),
232 max(nCells),
233 max(nFaces),
234 max(nPoints)
235 ))))+2
236 nameLen=max(len("Points"),boundMaxLen)
237
238 nrFormat ="%%%dd" % digits
239 nameFormat="%%%ds" % nameLen
240
241 print " "*nameLen,"|",
242 for i in range(sol.nrProcs()):
243 print nrFormat % i,
244 print
245
246 print "-"*(nameLen+3+(digits+1)*sol.nrProcs())
247
248 print nameFormat % "Points","|",
249 for p in nPoints:
250 print nrFormat % p,
251 print
252 print nameFormat % "Faces","|",
253 for p in nFaces:
254 print nrFormat % p,
255 print
256 print nameFormat % "Cells","|",
257 for p in nCells:
258 print nrFormat % p,
259 print
260
261 print "-"*(nameLen+3+(digits+1)*sol.nrProcs())
262
263 for b in boundaryNames:
264 print nameFormat % b,"|",
265 for p in sol.processorDirs():
266 print nrFormat % ParsedBoundaryDict(sol.boundaryDict(processor=p))[b]["nFaces"],
267 print
268
269 if self.opts.longBCreport:
270 print "\nThe boundary conditions for t =",time
271
272 for b in boundaryNames:
273 print "\nBoundary: \t",b
274 bound=boundary[b]
275 print " type:\t",bound["type"],
276 if "physicalType" in bound:
277 print "( Physical:",bound["physicalType"],")",
278 print " \t Faces:",bound["nFaces"]
279 for fName in fieldNames:
280 print " ",fName,
281 f=fields[fName]
282 if b not in f["boundaryField"]:
283 print " "*(nameMaxLen-len(fName)+2)+": MISSING !!!"
284 else:
285 bf=f["boundaryField"][b]
286 maxKeyLen=0
287 for k in bf:
288 maxKeyLen=max(maxKeyLen,len(k))
289
290 print " "*(nameMaxLen-len(fName)+2)+"type "+" "*(maxKeyLen-4)+": ",bf["type"]
291 for k in bf:
292 if k!="type":
293 print " "*(nameMaxLen+6),k," "*(maxKeyLen-len(k))+": ",
294 cont=str(bf[k])
295 if cont.find("\n")>=0:
296 print cont[:cont.find("\n")],"..."
297 else:
298 print cont
299
300 if self.opts.shortBCreport:
301 print "\nTable of boundary conditions for t =",time
302 print
303
304 colLen = {}
305 types={}
306 hasPhysical=False
307 nameMaxLen=max(nameMaxLen,len("Patch Type"))
308 for b in boundary:
309 colLen[b]=max(len(b),len(boundary[b]["type"]))
310 colLen[b]=max(len(b),len(str(boundary[b]["nFaces"])))
311 if "physicalType" in boundary[b]:
312 hasPhysical=True
313 nameMaxLen=max(nameMaxLen,len("Physical Type"))
314 colLen[b]=max(colLen[b],len(boundary[b]["physicalType"]))
315
316 types[b]={}
317
318 for fName in fields:
319 f=fields[fName]
320 if b not in f["boundaryField"]:
321 types[b][fName]="MISSING"
322 else:
323 types[b][fName]=f["boundaryField"][b]["type"]
324 colLen[b]=max(colLen[b],len(types[b][fName]))
325
326 print " "*(nameMaxLen),
327 nr=nameMaxLen+1
328 for b in boundaryNames:
329 print "| "+b+" "*(colLen[b]-len(b)),
330 nr+=colLen[b]+3
331 print
332 print "-"*nr
333 print "Patch Type"+" "*(nameMaxLen-len("Patch Type")),
334 for b in boundaryNames:
335 t=boundary[b]["type"]
336 print "| "+t+" "*(colLen[b]-len(t)),
337 print
338 if hasPhysical:
339 print "Physical Type"+" "*(nameMaxLen-len("Physical Type")),
340 for b in boundaryNames:
341 t=""
342 if "physicalType" in boundary[b]:
343 t=boundary[b]["physicalType"]
344 print "| "+t+" "*(colLen[b]-len(t)),
345 print
346 print "Length"+" "*(nameMaxLen-len("Length")),
347 for b in boundaryNames:
348 s=str(boundary[b]["nFaces"])
349 print "| "+s+" "*(colLen[b]-len(s)),
350 print
351 print "-"*nr
352 for fName in fieldNames:
353 print fName+" "*(nameMaxLen-len(fName)),
354 for b in boundaryNames:
355 t=types[b][fName]
356 print "| "+t+" "*(colLen[b]-len(t)),
357 print
358
359 print
360
361 if self.opts.dimensions:
362 print "\nDimensions of fields for t =",time
363 print
364
365 head="Name"+" "*(nameMaxLen-len("Name"))+" : [kg m s K mol A cd]"
366 print head
367 print "-"*len(head)
368 for fName in fieldNames:
369 f=fields[fName]
370
371 print fName+" "*(nameMaxLen-len(fName))+" :",f["dimensions"]
372
373 if self.opts.internal:
374 print "\Internal value of fields for t =",time
375 print
376
377 head="Name"+" "*(nameMaxLen-len("Name"))+" : Value "
378 print head
379 print "-"*len(head)
380 for fName in fieldNames:
381 f=fields[fName]
382
383 print fName+" "*(nameMaxLen-len(fName))+" :",
384
385 cont=str(f["internalField"])
386 if cont.find("\n")>=0:
387 print cont[:cont.find("\n")],"..."
388 else:
389 print cont
390
391 if self.opts.processorMatrix:
392 if sol.nrProcs()<2:
393 error("The case is not decomposed")
394
395 matrix=[ [0,]*sol.nrProcs() for i in range(sol.nrProcs())]
396
397 for i,p in enumerate(sol.processorDirs()):
398 bound=ParsedBoundaryDict(path.join(sol.name,p,procTime,"polyMesh","boundary"))
399 for j in range(sol.nrProcs()):
400 name="procBoundary%dto%d" %(i,j)
401 if name in bound:
402 matrix[i][j]=bound[name]["nFaces"]
403
404 print "Matrix of processor interactions (faces)"
405 print
406
407 digits=int(ceil(log10(sol.nrProcs())))+2
408 colDigits=int(ceil(log10(max(digits,max(max(matrix))))))+2
409
410 format="%%%dd" % digits
411 colFormat="%%%dd" % colDigits
412
413 print " "*(digits),"|",
414 for j in range(sol.nrProcs()):
415 print colFormat % j,
416 print
417 print "-"*(digits+3+(colDigits+1)*sol.nrProcs())
418
419 for i,col in enumerate(matrix):
420 print format % i,"|",
421 for j,nr in enumerate(col):
422 print colFormat % matrix[i][j],
423 print
424