Package PyFoam :: Package Applications :: Module CaseReport
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Applications.CaseReport

  1  #  ICE Revision: $Id$ 
  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  from PyFoam.Basics.RestructuredTextHelper import RestructuredTextHelper 
 17  from PyFoam.Basics.DataStructures import DictProxy,Field 
 18   
 19  from PyFoam.Error import error,warning 
 20   
 21  from PyFoam.ThirdParty.six import print_,iteritems 
 22   
 23  from math import log10,ceil 
 24  from os import path 
 25   
 26  import sys 
 27   
28 -class CaseReport(PyFoamApplication):
29 - def __init__(self,args=None):
30 description="""\ 31 Produces human-readable reports about a case. Attention: the amount of 32 information in the reports is limited. The truth is always in the 33 dictionary-files. 34 35 The format of the output is restructured-text so it can be run through 36 a postprocessor like rst2tex or rst2html to produce PDF or HTML 37 respectivly 38 """ 39 40 PyFoamApplication.__init__(self, 41 args=args, 42 description=description, 43 usage="%prog [options] <casedir>", 44 nr=1, 45 changeVersion=False, 46 interspersed=True)
47
48 - def addOptions(self):
49 report=OptionGroup(self.parser, 50 "Reports", 51 "What kind of reports should be produced") 52 self.parser.add_option_group(report) 53 select=OptionGroup(self.parser, 54 "Selection", 55 "Which data should be used for the reports") 56 self.parser.add_option_group(select) 57 internal=OptionGroup(self.parser, 58 "Internal", 59 "Details of the parser") 60 self.parser.add_option_group(internal) 61 62 format=OptionGroup(self.parser, 63 "Formatting", 64 "Restructured Text formatting") 65 self.parser.add_option_group(format) 66 67 format.add_option("--heading-level", 68 action="store", 69 type="int", 70 default=2, 71 dest="headingLevel", 72 help="Default level of the headings. Valid values from 0 to 5. Default: %default") 73 74 output=OptionGroup(self.parser, 75 "Output", 76 "How Output should be generated") 77 self.parser.add_option_group(output) 78 79 output.add_option("--file", 80 action="store", 81 default=None, 82 dest="file", 83 help="Write the output to a file instead of the console") 84 85 report.add_option("--full-report", 86 action="store_true", 87 default=False, 88 dest="all", 89 help="Print all available reports at once") 90 91 report.add_option("--short-bc-report", 92 action="store_true", 93 default=False, 94 dest="shortBCreport", 95 help="Gives a short overview of the boundary-conditions in the case") 96 97 report.add_option("--long-bc-report", 98 action="store_true", 99 default=False, 100 dest="longBCreport", 101 help="Gives a full overview of the boundary-conditions in the case") 102 103 report.add_option("--dimensions", 104 action="store_true", 105 default=False, 106 dest="dimensions", 107 help="Show the dimensions of the fields") 108 109 report.add_option("--internal-field", 110 action="store_true", 111 default=False, 112 dest="internal", 113 help="Show the internal value of the fields (the initial conditions)") 114 115 report.add_option("--linear-solvers", 116 action="store_true", 117 default=False, 118 dest="linearSolvers", 119 help="Print the linear solvers and their tolerance") 120 121 report.add_option("--relaxation-factors", 122 action="store_true", 123 default=False, 124 dest="relaxationFactors", 125 help="Print the relaxation factors (if there are any)") 126 127 select.add_option("--time", 128 action="store", 129 type="float", 130 default=None, 131 dest="time", 132 help="Time to use as the basis for the reports") 133 134 select.add_option("--region", 135 dest="region", 136 default=None, 137 help="Do the report for a special region for multi-region cases") 138 139 select.add_option("--all-regions", 140 dest="allRegions", 141 action="store_true", 142 default=False, 143 help="Do the report for all regions for multi-region cases") 144 145 select.add_option("--parallel", 146 action="store_true", 147 default=False, 148 dest="parallel", 149 help="Get times from the processor-directories") 150 151 internal.add_option("--long-field-threshold", 152 action="store", 153 type="int", 154 default=100, 155 dest="longlist", 156 help="Fields that are longer than this won't be parsed, but read into memory (and compared as strings). Default: %default") 157 internal.add_option("--no-do-macro-expansion", 158 action="store_false", 159 default=True, 160 dest="doMacros", 161 help="Don't expand macros with $ and # in the field-files") 162 163 internal.add_option("--treat-binary-as-ascii", 164 action="store_true", 165 default=False, 166 dest="treatBinaryAsASCII", 167 help="Try to treat binary dictionaries as ASCII anyway") 168 169 select.add_option("--patches", 170 action="append", 171 default=None, 172 dest="patches", 173 help="Patches which should be processed (pattern, can be used more than once)") 174 175 select.add_option("--exclude-patches", 176 action="append", 177 default=None, 178 dest="expatches", 179 help="Patches which should not be processed (pattern, can be used more than once)") 180 181 report.add_option("--processor-matrix", 182 action="store_true", 183 default=False, 184 dest="processorMatrix", 185 help="Prints the matrix how many faces from one processor interact with another") 186 187 report.add_option("--case-size", 188 action="store_true", 189 default=False, 190 dest="caseSize", 191 help="Report the number of cells, points and faces in the case") 192 193 report.add_option("--decomposition", 194 action="store_true", 195 default=False, 196 dest="decomposition", 197 help="Reports the size of the parallel decomposition")
198
199 - def run(self):
200 if self.opts.file: 201 sys.stdout=open(self.opts.file,"w") 202 203 if self.opts.allRegions: 204 sol=SolutionDirectory(self.parser.getArgs()[0], 205 archive=None, 206 parallel=self.opts.parallel, 207 paraviewLink=False) 208 for r in sol.getRegions(): 209 self.doRegion(r) 210 else: 211 self.doRegion(self.opts.region)
212
213 - def doRegion(self,theRegion):
214 ReST=RestructuredTextHelper(defaultHeading=self.opts.headingLevel) 215 216 if self.opts.allRegions: 217 print_(ReST.buildHeading("Region: ",theRegion,level=self.opts.headingLevel-1)) 218 219 sol=SolutionDirectory(self.parser.getArgs()[0], 220 archive=None, 221 parallel=self.opts.parallel, 222 paraviewLink=False, 223 region=theRegion) 224 225 if self.opts.all: 226 self.opts.caseSize=True 227 self.opts.shortBCreport=True 228 self.opts.longBCreport=True 229 self.opts.dimensions=True 230 self.opts.internal=True 231 self.opts.linearSolvers=True 232 self.opts.relaxationFactors=True 233 self.opts.processorMatrix=True 234 self.opts.decomposition=True 235 236 if self.opts.time: 237 try: 238 self.opts.time=sol.timeName(sol.timeIndex(self.opts.time,minTime=True)) 239 except IndexError: 240 error("The specified time",self.opts.time,"doesn't exist in the case") 241 print_("Using time t="+self.opts.time+"\n") 242 243 needsPolyBoundaries=False 244 needsInitialTime=False 245 246 if self.opts.longBCreport: 247 needsPolyBoundaries=True 248 needsInitialTime=True 249 if self.opts.shortBCreport: 250 needsPolyBoundaries=True 251 needsInitialTime=True 252 if self.opts.dimensions: 253 needsInitialTime=True 254 if self.opts.internal: 255 needsInitialTime=True 256 if self.opts.decomposition: 257 needsPolyBoundaries=True 258 259 defaultProc=None 260 if self.opts.parallel: 261 defaultProc=0 262 263 if needsPolyBoundaries: 264 proc=None 265 boundary=BoundaryDict(sol.name, 266 region=theRegion, 267 time=self.opts.time, 268 treatBinaryAsASCII=self.opts.treatBinaryAsASCII, 269 processor=defaultProc) 270 271 boundMaxLen=0 272 boundaryNames=[] 273 for b in boundary: 274 if b.find("procBoundary")!=0: 275 boundaryNames.append(b) 276 if self.opts.patches!=None: 277 tmp=boundaryNames 278 boundaryNames=[] 279 for b in tmp: 280 for p in self.opts.patches: 281 if fnmatch(b,p): 282 boundaryNames.append(b) 283 break 284 285 if self.opts.expatches!=None: 286 tmp=boundaryNames 287 boundaryNames=[] 288 for b in tmp: 289 keep=True 290 for p in self.opts.expatches: 291 if fnmatch(b,p): 292 keep=False 293 break 294 if keep: 295 boundaryNames.append(b) 296 297 for b in boundaryNames: 298 boundMaxLen=max(boundMaxLen,len(b)) 299 boundaryNames.sort() 300 301 if self.opts.time==None: 302 procTime="constant" 303 else: 304 procTime=self.opts.time 305 306 if needsInitialTime: 307 fields={} 308 309 if self.opts.time==None: 310 try: 311 time=sol.timeName(0) 312 except IndexError: 313 error("There is no timestep in the case") 314 else: 315 time=self.opts.time 316 317 tDir=sol[time] 318 319 nameMaxLen=0 320 321 for f in tDir: 322 try: 323 fields[f.baseName()]=f.getContent(listLengthUnparsed=self.opts.longlist, 324 treatBinaryAsASCII=self.opts.treatBinaryAsASCII, 325 doMacroExpansion=self.opts.doMacros) 326 nameMaxLen=max(nameMaxLen,len(f.baseName())) 327 except PyFoamParserError: 328 e = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e' 329 warning("Couldn't parse",f.name,"because of an error:",e," -> skipping") 330 331 fieldNames=list(fields.keys()) 332 fieldNames.sort() 333 334 if self.opts.caseSize: 335 print_(ReST.heading("Size of the case")) 336 337 nFaces=0 338 nPoints=0 339 nCells=0 340 if self.opts.parallel: 341 procs=list(range(sol.nrProcs())) 342 print_("Accumulated from",sol.nrProcs(),"processors") 343 else: 344 procs=[None] 345 346 for p in procs: 347 info=MeshInformation(sol.name, 348 processor=p, 349 region=theRegion, 350 time=self.opts.time) 351 nFaces+=info.nrOfFaces() 352 nPoints+=info.nrOfPoints() 353 try: 354 nCells+=info.nrOfCells() 355 except: 356 nCells="Not available" 357 tab=ReST.table() 358 tab[0]=("Faces",nFaces) 359 tab[1]=("Points",nPoints) 360 tab[2]=("Cells",nCells) 361 print_(tab) 362 363 if self.opts.decomposition: 364 print_(ReST.heading("Decomposition")) 365 366 if sol.nrProcs()<2: 367 print_("This case is not decomposed") 368 else: 369 print_("Case is decomposed for",sol.nrProcs(),"processors") 370 print_() 371 372 nCells=[] 373 nFaces=[] 374 nPoints=[] 375 for p in sol.processorDirs(): 376 info=MeshInformation(sol.name, 377 processor=p, 378 region=theRegion, 379 time=self.opts.time) 380 nPoints.append(info.nrOfPoints()) 381 nFaces.append(info.nrOfFaces()) 382 nCells.append(info.nrOfCells()) 383 384 digits=int(ceil(log10(max(sol.nrProcs(), 385 max(nCells), 386 max(nFaces), 387 max(nPoints) 388 ))))+2 389 nameLen=max(len("Points"),boundMaxLen) 390 391 tab=ReST.table() 392 tab[0]=["CPU"]+list(range(sol.nrProcs())) 393 394 tab.addLine() 395 396 tab[1]=["Points"]+nPoints 397 tab[2]=["Faces"]+nFaces 398 tab[3]=["Cells"]+nCells 399 tab.addLine(head=True) 400 401 nr=3 402 for b in boundaryNames: 403 nr+=1 404 tab[(nr,0)]=b 405 for i,p in enumerate(sol.processorDirs()): 406 try: 407 nFaces= ParsedBoundaryDict(sol.boundaryDict(processor=p, 408 region=theRegion, 409 time=self.opts.time), 410 treatBinaryAsASCII=self.opts.treatBinaryAsASCII 411 )[b]["nFaces"] 412 except IOError: 413 nFaces= ParsedBoundaryDict(sol.boundaryDict(processor=p, 414 region=theRegion), 415 treatBinaryAsASCII=self.opts.treatBinaryAsASCII 416 )[b]["nFaces"] 417 except KeyError: 418 nFaces=0 419 420 tab[(nr,i+1)]=nFaces 421 422 print_(tab) 423 424 if self.opts.longBCreport: 425 print_(ReST.heading("The boundary conditions for t =",time)) 426 427 for b in boundaryNames: 428 print_(ReST.buildHeading("Boundary: ",b,level=self.opts.headingLevel+1)) 429 bound=boundary[b] 430 print_(":Type:\t",bound["type"]) 431 if "physicalType" in bound: 432 print_(":Physical:\t",bound["physicalType"]) 433 print_(":Faces:\t",bound["nFaces"]) 434 print_() 435 heads=["Field","type"] 436 tab=ReST.table() 437 tab[0]=heads 438 tab.addLine(head=True) 439 for row,fName in enumerate(fieldNames): 440 tab[(row+1,0)]=fName 441 f=fields[fName] 442 if "boundaryField" not in f: 443 tab[(row+1,1)]="Not a field file" 444 elif b not in f["boundaryField"]: 445 tab[(row+1,1)]="MISSING !!!" 446 else: 447 bf=f["boundaryField"][b] 448 449 for k in bf: 450 try: 451 col=heads.index(k) 452 except ValueError: 453 col=len(heads) 454 tab[(0,col)]=k 455 heads.append(k) 456 cont=str(bf[k]) 457 if type(bf[k])==Field: 458 if bf[k].isBinary(): 459 cont= bf[k].binaryString() 460 461 if cont.find("\n")>=0: 462 tab[(row+1,col)]=cont[:cont.find("\n")]+"..." 463 else: 464 tab[(row+1,col)]=cont 465 print_(tab) 466 467 if self.opts.shortBCreport: 468 print_(ReST.heading("Table of boundary conditions for t =",time)) 469 470 types={} 471 hasPhysical=False 472 for b in boundary: 473 if "physicalType" in boundary[b]: 474 hasPhysical=True 475 476 types[b]={} 477 478 for fName in fields: 479 f=fields[fName] 480 try: 481 if b not in f["boundaryField"]: 482 types[b][fName]="MISSING" 483 else: 484 types[b][fName]=f["boundaryField"][b]["type"] 485 except KeyError: 486 types[b][fName]="Not a field" 487 488 tab=ReST.table() 489 tab[0]=[""]+boundaryNames 490 tab.addLine() 491 tab[(1,0)]="Patch Type" 492 for i,b in enumerate(boundaryNames): 493 tab[(1,i+1)]=boundary[b]["type"] 494 495 nr=2 496 if hasPhysical: 497 tab[(nr,0)]="Physical Type" 498 for i,b in enumerate(boundaryNames): 499 if "physicalType" in boundary[b]: 500 tab[(nr,i+1)]=boundary[b]["physicalType"] 501 nr+=1 502 503 tab[(nr,0)]="Length" 504 for i,b in enumerate(boundaryNames): 505 tab[(nr,i+1)]=boundary[b]["nFaces"] 506 nr+=1 507 tab.addLine(head=True) 508 509 for fName in fieldNames: 510 tab[(nr,0)]=fName 511 for i,b in enumerate(boundaryNames): 512 tab[(nr,i+1)]=types[b][fName] 513 nr+=1 514 515 print_(tab) 516 517 if self.opts.dimensions: 518 print_(ReST.heading("Dimensions of fields for t =",time)) 519 520 tab=ReST.table() 521 tab[0]=["Name"]+"[ kg m s K mol A cd ]".split()[1:-1] 522 tab.addLine(head=True) 523 for i,fName in enumerate(fieldNames): 524 f=fields[fName] 525 try: 526 dim=str(f["dimensions"]).split()[1:-1] 527 except KeyError: 528 dim=["-"]*7 529 tab[i+1]=[fName]+dim 530 print_(tab) 531 532 if self.opts.internal: 533 print_(ReST.heading("Internal value of fields for t =",time)) 534 535 tab=ReST.table() 536 tab[0]=["Name","Value"] 537 tab.addLine(head=True) 538 for i,fName in enumerate(fieldNames): 539 f=fields[fName] 540 541 try: 542 if f["internalField"].isBinary(): 543 val=f["internalField"].binaryString() 544 else: 545 cont=str(f["internalField"]) 546 if cont.find("\n")>=0: 547 val=cont[:cont.find("\n")]+"..." 548 else: 549 val=cont 550 except KeyError: 551 val="Not a field file" 552 tab[i+1]=[fName,val] 553 print_(tab) 554 555 if self.opts.processorMatrix: 556 print_(ReST.heading("Processor matrix")) 557 558 if sol.nrProcs()<2: 559 print_("This case is not decomposed") 560 else: 561 matrix=[ [0,]*sol.nrProcs() for i in range(sol.nrProcs())] 562 563 for i,p in enumerate(sol.processorDirs()): 564 try: 565 bound=ParsedBoundaryDict(sol.boundaryDict(processor=p, 566 region=theRegion, 567 time=self.opts.time) 568 ,treatBinaryAsASCII=self.opts.treatBinaryAsASCII) 569 except IOError: 570 bound=ParsedBoundaryDict(sol.boundaryDict(processor=p, 571 treatBinaryAsASCII=self.opts.treatBinaryAsASCII, 572 region=theRegion) 573 ,treatBinaryAsASCII=self.opts.treatBinaryAsASCII) 574 575 for j in range(sol.nrProcs()): 576 name="procBoundary%dto%d" %(j,i) 577 name2="procBoundary%dto%d" %(i,j) 578 if name in bound: 579 matrix[i][j]=bound[name]["nFaces"] 580 if name2 in bound: 581 matrix[i][j]=bound[name2]["nFaces"] 582 583 print_("Matrix of processor interactions (faces)") 584 print_() 585 586 tab=ReST.table() 587 tab[0]=["CPU"]+list(range(sol.nrProcs())) 588 tab.addLine(head=True) 589 590 for i,col in enumerate(matrix): 591 tab[i+1]=[i]+matrix[i] 592 593 print_(tab) 594 595 if self.opts.linearSolvers: 596 print_(ReST.heading("Linear Solvers")) 597 598 linTable=ReST.table() 599 600 fvSol=ParsedParameterFile(path.join(sol.systemDir(),"fvSolution"), 601 treatBinaryAsASCII=self.opts.treatBinaryAsASCII) 602 allInfo={} 603 for sName in fvSol["solvers"]: 604 raw=fvSol["solvers"][sName] 605 info={} 606 if type(raw) in [dict,DictProxy]: 607 # fvSolution format in 1.7 608 info["solver"]=raw["solver"] 609 solverData=raw 610 else: 611 info["solver"]=raw[0] 612 solverData=raw[1] 613 614 if type(solverData) in [dict,DictProxy]: 615 try: 616 info["tolerance"]=solverData["tolerance"] 617 except KeyError: 618 info["tolerance"]=1. 619 try: 620 info["relTol"]=solverData["relTol"] 621 except KeyError: 622 info["relTol"]=0. 623 else: 624 # the old (pre-1.5) fvSolution-format 625 info["tolerance"]=solverData 626 info["relTol"]=raw[2] 627 628 allInfo[sName]=info 629 630 linTable[0]=["Name","Solver","Abs. Tolerance","Relative Tol."] 631 linTable.addLine(head=True) 632 633 nr=0 634 for n,i in iteritems(allInfo): 635 nr+=1 636 linTable[nr]=(n,i["solver"],i["tolerance"],i["relTol"]) 637 print_(linTable) 638 639 if self.opts.relaxationFactors: 640 print_(ReST.heading("Relaxation")) 641 642 fvSol=ParsedParameterFile(path.join(sol.systemDir(),"fvSolution"), 643 treatBinaryAsASCII=self.opts.treatBinaryAsASCII) 644 if "relaxationFactors" in fvSol: 645 tab=ReST.table() 646 tab[0]=["Name","Factor"] 647 tab.addLine(head=True) 648 nr=0 649 for n,f in iteritems(fvSol["relaxationFactors"]): 650 nr+=1 651 tab[nr]=[n,f] 652 print_(tab) 653 else: 654 print_("No relaxation factors defined for this case")
655 656 # Should work with Python3 and Python2 657