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,ParsedParameterFile
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 output=OptionGroup(self.parser,
52 "Output",
53 "How Output should be generated")
54 self.parser.add_option_group(output)
55
56 output.add_option("--file",
57 action="store",
58 default=None,
59 dest="file",
60 help="Write the output to a file instead of the console")
61
62 report.add_option("--short-bc-report",
63 action="store_true",
64 default=False,
65 dest="shortBCreport",
66 help="Gives a short overview of the boundary-conditions in the case")
67
68 report.add_option("--long-bc-report",
69 action="store_true",
70 default=False,
71 dest="longBCreport",
72 help="Gives a full overview of the boundary-conditions in the case")
73
74 report.add_option("--dimensions",
75 action="store_true",
76 default=False,
77 dest="dimensions",
78 help="Show the dimensions of the fields")
79
80 report.add_option("--internal-field",
81 action="store_true",
82 default=False,
83 dest="internal",
84 help="Show the internal value of the fields (the initial conditions)")
85
86 report.add_option("--linear-solvers",
87 action="store_true",
88 default=False,
89 dest="linearSolvers",
90 help="Print the linear solvers and their tolerance")
91
92 report.add_option("--relaxation-factors",
93 action="store_true",
94 default=False,
95 dest="relaxationFactors",
96 help="Print the relaxation factors (if there are any)")
97
98 select.add_option("--time",
99 action="store",
100 type="float",
101 default=None,
102 dest="time",
103 help="Time to use as the basis for the reports")
104
105 select.add_option("--region",
106 dest="region",
107 default=None,
108 help="Do the report for a special region for multi-region cases")
109
110 select.add_option("--parallel",
111 action="store_true",
112 default=False,
113 dest="parallel",
114 help="Print the relaxation factors (if there are any)")
115
116 internal.add_option("--long-field-threshold",
117 action="store",
118 type="int",
119 default=100,
120 dest="longlist",
121 help="Fields that are longer than this won't be parsed, but read into memory (and compared as strings)")
122
123 select.add_option("--patches",
124 action="append",
125 default=None,
126 dest="patches",
127 help="Patches which should be processed (pattern, can be used more than once)")
128
129 select.add_option("--exclude-patches",
130 action="append",
131 default=None,
132 dest="expatches",
133 help="Patches which should not be processed (pattern, can be used more than once)")
134
135 report.add_option("--processor-matrix",
136 action="store_true",
137 default=False,
138 dest="processorMatrix",
139 help="Prints the matrix how many faces from one processor interact with another")
140
141 report.add_option("--case-size",
142 action="store_true",
143 default=False,
144 dest="caseSize",
145 help="Report the number of cells, points and faces in the case")
146
147 report.add_option("--decomposition",
148 action="store_true",
149 default=False,
150 dest="decomposition",
151 help="Reports the size of the parallel decomposition")
152
154 if self.opts.file:
155 sys.stdout=open(self.opts.file,"w")
156
157 sol=SolutionDirectory(self.parser.getArgs()[0],
158 archive=None,
159 parallel=self.opts.parallel,
160 paraviewLink=False,
161 region=self.opts.region)
162 if self.opts.time:
163 try:
164 self.opts.time=sol.timeName(sol.timeIndex(self.opts.time,minTime=True))
165 except IndexError:
166 error("The specified time",self.opts.time,"doesn't exist in the case")
167 print "Using time t="+self.opts.time+"\n"
168
169 needsPolyBoundaries=False
170 needsInitialTime=False
171
172 if self.opts.longBCreport:
173 needsPolyBoundaries=True
174 needsInitialTime=True
175 if self.opts.shortBCreport:
176 needsPolyBoundaries=True
177 needsInitialTime=True
178 if self.opts.dimensions:
179 needsInitialTime=True
180 if self.opts.internal:
181 needsInitialTime=True
182 if self.opts.decomposition:
183 needsPolyBoundaries=True
184
185 defaultProc=None
186 if self.opts.parallel:
187 defaultProc=0
188
189 if needsPolyBoundaries:
190 proc=None
191 boundary=BoundaryDict(sol.name,
192 region=self.opts.region,
193 time=self.opts.time,
194 processor=defaultProc)
195
196 boundMaxLen=0
197 boundaryNames=[]
198 for b in boundary:
199 if b.find("procBoundary")!=0:
200 boundaryNames.append(b)
201 if self.opts.patches!=None:
202 tmp=boundaryNames
203 boundaryNames=[]
204 for b in tmp:
205 for p in self.opts.patches:
206 if fnmatch(b,p):
207 boundaryNames.append(b)
208 break
209
210 if self.opts.expatches!=None:
211 tmp=boundaryNames
212 boundaryNames=[]
213 for b in tmp:
214 keep=True
215 for p in self.opts.expatches:
216 if fnmatch(b,p):
217 keep=False
218 break
219 if keep:
220 boundaryNames.append(b)
221
222 for b in boundaryNames:
223 boundMaxLen=max(boundMaxLen,len(b))
224 boundaryNames.sort()
225
226 if self.opts.time==None:
227 procTime="constant"
228 else:
229 procTime=self.opts.time
230
231 if needsInitialTime:
232 fields={}
233
234 if self.opts.time==None:
235 try:
236 time=sol.timeName(0)
237 except IndexError:
238 error("There is no timestep in the case")
239 else:
240 time=self.opts.time
241
242 tDir=sol[time]
243
244 nameMaxLen=0
245
246 for f in tDir:
247 try:
248 fields[f.baseName()]=f.getContent(listLengthUnparsed=self.opts.longlist)
249 nameMaxLen=max(nameMaxLen,len(f.baseName()))
250 except PyFoamParserError,e:
251 warning("Couldn't parse",f.name,"because of an error:",e," -> skipping")
252
253 fieldNames=fields.keys()
254 fieldNames.sort()
255
256 if self.opts.caseSize:
257 print "Size of the case"
258 nFaces=0
259 nPoints=0
260 nCells=0
261 if self.opts.parallel:
262 procs=range(sol.nrProcs())
263 print "Accumulated from",sol.nrProcs(),"processors"
264 else:
265 procs=[None]
266 print
267
268 for p in procs:
269 info=MeshInformation(sol.name,
270 processor=p,
271 time=self.opts.time)
272 nFaces+=info.nrOfFaces()
273 nPoints+=info.nrOfPoints()
274 try:
275 nCells+=info.nrOfCells()
276 except:
277 nCells="Not available"
278 print "Faces: \t",nFaces
279 print "Points: \t",nPoints
280 print "Cells: \t",nCells
281
282 if self.opts.decomposition:
283 if sol.nrProcs()<2:
284 print "This case is not decomposed"
285 return
286
287 print "Case is decomposed for",sol.nrProcs(),"processors"
288
289 nCells=[]
290 nFaces=[]
291 nPoints=[]
292 for p in sol.processorDirs():
293 info=MeshInformation(sol.name,
294 processor=p,
295 time=self.opts.time)
296 nPoints.append(info.nrOfPoints())
297 nFaces.append(info.nrOfFaces())
298 nCells.append(info.nrOfCells())
299
300 digits=int(ceil(log10(max(sol.nrProcs(),
301 max(nCells),
302 max(nFaces),
303 max(nPoints)
304 ))))+2
305 nameLen=max(len("Points"),boundMaxLen)
306
307 nrFormat ="%%%dd" % digits
308 nameFormat="%%%ds" % nameLen
309
310 print " "*nameLen,"|",
311 for i in range(sol.nrProcs()):
312 print nrFormat % i,
313 print
314
315 print "-"*(nameLen+3+(digits+1)*sol.nrProcs())
316
317 print nameFormat % "Points","|",
318 for p in nPoints:
319 print nrFormat % p,
320 print
321 print nameFormat % "Faces","|",
322 for p in nFaces:
323 print nrFormat % p,
324 print
325 print nameFormat % "Cells","|",
326 for p in nCells:
327 print nrFormat % p,
328 print
329
330 print "-"*(nameLen+3+(digits+1)*sol.nrProcs())
331
332 for b in boundaryNames:
333 print nameFormat % b,"|",
334 for p in sol.processorDirs():
335 try:
336 nFaces= ParsedBoundaryDict(sol.boundaryDict(processor=p,
337 time=self.opts.time)
338 )[b]["nFaces"]
339 except KeyError:
340 nFaces=0
341
342 print nrFormat % nFaces,
343 print
344
345 if self.opts.longBCreport:
346 print "\nThe boundary conditions for t =",time
347
348 for b in boundaryNames:
349 print "\nBoundary: \t",b
350 bound=boundary[b]
351 print " type:\t",bound["type"],
352 if "physicalType" in bound:
353 print "( Physical:",bound["physicalType"],")",
354 print " \t Faces:",bound["nFaces"]
355 for fName in fieldNames:
356 print " ",fName,
357 f=fields[fName]
358 if "boundaryField" not in f:
359 print " "*(nameMaxLen-len(fName)+2)+": Not a field file"
360 elif b not in f["boundaryField"]:
361 print " "*(nameMaxLen-len(fName)+2)+": MISSING !!!"
362 else:
363 bf=f["boundaryField"][b]
364 maxKeyLen=0
365 for k in bf:
366 maxKeyLen=max(maxKeyLen,len(k))
367
368 print " "*(nameMaxLen-len(fName)+2)+"type "+" "*(maxKeyLen-4)+": ",bf["type"]
369 for k in bf:
370 if k!="type":
371 print " "*(nameMaxLen+6),k," "*(maxKeyLen-len(k))+": ",
372 cont=str(bf[k])
373 if cont.find("\n")>=0:
374 print cont[:cont.find("\n")],"..."
375 else:
376 print cont
377
378 if self.opts.shortBCreport:
379 print "\nTable of boundary conditions for t =",time
380 print
381
382 colLen = {}
383 types={}
384 hasPhysical=False
385 nameMaxLen=max(nameMaxLen,len("Patch Type"))
386 for b in boundary:
387 colLen[b]=max(len(b),len(boundary[b]["type"]))
388 colLen[b]=max(len(b),len(str(boundary[b]["nFaces"])))
389 if "physicalType" in boundary[b]:
390 hasPhysical=True
391 nameMaxLen=max(nameMaxLen,len("Physical Type"))
392 colLen[b]=max(colLen[b],len(boundary[b]["physicalType"]))
393
394 types[b]={}
395
396 for fName in fields:
397 f=fields[fName]
398 try:
399 if b not in f["boundaryField"]:
400 types[b][fName]="MISSING"
401 else:
402 types[b][fName]=f["boundaryField"][b]["type"]
403 except KeyError:
404 types[b][fName]="Not a field"
405
406 colLen[b]=max(colLen[b],len(types[b][fName]))
407
408 print " "*(nameMaxLen),
409 nr=nameMaxLen+1
410 for b in boundaryNames:
411 print "| "+b+" "*(colLen[b]-len(b)),
412 nr+=colLen[b]+3
413 print
414 print "-"*nr
415 print "Patch Type"+" "*(nameMaxLen-len("Patch Type")),
416 for b in boundaryNames:
417 t=boundary[b]["type"]
418 print "| "+t+" "*(colLen[b]-len(t)),
419 print
420 if hasPhysical:
421 print "Physical Type"+" "*(nameMaxLen-len("Physical Type")),
422 for b in boundaryNames:
423 t=""
424 if "physicalType" in boundary[b]:
425 t=boundary[b]["physicalType"]
426 print "| "+t+" "*(colLen[b]-len(t)),
427 print
428 print "Length"+" "*(nameMaxLen-len("Length")),
429 for b in boundaryNames:
430 s=str(boundary[b]["nFaces"])
431 print "| "+s+" "*(colLen[b]-len(s)),
432 print
433 print "-"*nr
434 for fName in fieldNames:
435 print fName+" "*(nameMaxLen-len(fName)),
436 for b in boundaryNames:
437 t=types[b][fName]
438 print "| "+t+" "*(colLen[b]-len(t)),
439 print
440
441 print
442
443 if self.opts.dimensions:
444 print "\nDimensions of fields for t =",time
445 print
446
447 head="Name"+" "*(nameMaxLen-len("Name"))+" : [kg m s K mol A cd]"
448 print head
449 print "-"*len(head)
450 for fName in fieldNames:
451 f=fields[fName]
452 try:
453 dim=f["dimensions"]
454 print fName+" "*(nameMaxLen-len(fName))+" :",dim
455 except KeyError:
456 print fName,"is not a field file"
457
458 if self.opts.internal:
459 print "\Internal value of fields for t =",time
460 print
461
462 head="Name"+" "*(nameMaxLen-len("Name"))+" : Value "
463 print head
464 print "-"*len(head)
465 for fName in fieldNames:
466 f=fields[fName]
467
468 print fName+" "*(nameMaxLen-len(fName))+" :",
469
470 try:
471 cont=str(f["internalField"])
472 if cont.find("\n")>=0:
473 print cont[:cont.find("\n")],"..."
474 else:
475 print cont
476 except KeyError:
477 print "Not a field file"
478
479 if self.opts.processorMatrix:
480 if sol.nrProcs()<2:
481 print "This case is not decomposed"
482
483 return
484
485 matrix=[ [0,]*sol.nrProcs() for i in range(sol.nrProcs())]
486
487 for i,p in enumerate(sol.processorDirs()):
488 bound=ParsedBoundaryDict(path.join(sol.name,p,procTime,"polyMesh","boundary"))
489 for j in range(sol.nrProcs()):
490 name="procBoundary%dto%d" %(j,i)
491 name2="procBoundary%dto%d" %(i,j)
492 if name in bound:
493 matrix[i][j]=bound[name]["nFaces"]
494 if name2 in bound:
495 matrix[i][j]=bound[name2]["nFaces"]
496
497 print "Matrix of processor interactions (faces)"
498 print
499
500 digits=int(ceil(log10(sol.nrProcs())))+2
501 colDigits=int(ceil(log10(max(digits,max(max(matrix))))))+2
502
503 format="%%%dd" % digits
504 colFormat="%%%dd" % colDigits
505
506 print " "*(digits),"|",
507 for j in range(sol.nrProcs()):
508 print colFormat % j,
509 print
510 print "-"*(digits+3+(colDigits+1)*sol.nrProcs())
511
512 for i,col in enumerate(matrix):
513 print format % i,"|",
514 for j,nr in enumerate(col):
515 print colFormat % matrix[i][j],
516 print
517
518 if self.opts.linearSolvers:
519 fvSol=ParsedParameterFile(path.join(sol.systemDir(),"fvSolution"))
520 allInfo={}
521 for sName in fvSol["solvers"]:
522 raw=fvSol["solvers"][sName]
523 info={}
524 info["solver"]=raw[0]
525 try:
526 info["tolerance"]=raw[1]["tolerance"]
527 except KeyError:
528 info["tolerance"]=1.
529 try:
530 info["relTol"]=raw[1]["relTol"]
531 except KeyError:
532 info["relTol"]=0.
533
534 allInfo[sName]=info
535
536 title="%15s | %15s | %10s | %8s" % ("name","solver","tolerance","relative")
537 print title
538 print "-"*len(title)
539 for n,i in allInfo.iteritems():
540 print "%15s | %15s | %10g | %8g" % (n,i["solver"],i["tolerance"],i["relTol"])
541 print
542
543 if self.opts.relaxationFactors:
544 fvSol=ParsedParameterFile(path.join(sol.systemDir(),"fvSolution"))
545 if "relaxationFactors" in fvSol:
546 title="%20s | %15s" % ("name","factor")
547 print title
548 print "-"*len(title)
549 for n,f in fvSol["relaxationFactors"].iteritems():
550 print "%20s | %15g" % (n,f)
551 print
552 else:
553 print "No relaxation factors defined for this case"
554