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

Source Code for Module PyFoam.Applications.Decomposer

  1  #  ICE Revision: $Id$ 
  2  """ 
  3  Class that implements pyFoamDecompose 
  4  """ 
  5   
  6  from optparse import OptionGroup 
  7   
  8  from .PyFoamApplication import PyFoamApplication 
  9  from PyFoam.Basics.FoamFileGenerator import FoamFileGenerator 
 10  from PyFoam.Error import error 
 11  from PyFoam.Basics.Utilities import writeDictionaryHeader,rmtree 
 12  from PyFoam.Execution.UtilityRunner import UtilityRunner 
 13  from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory 
 14  from PyFoam.RunDictionary.RegionCases import RegionCases 
 15  from PyFoam.RunDictionary.ParsedParameterFile import FoamStringParser 
 16  from PyFoam.FoamInformation import oldAppConvention as oldApp 
 17  from PyFoam.FoamInformation import foamVersion 
 18   
 19  from .CommonMultiRegion import CommonMultiRegion 
 20  from .CommonStandardOutput import CommonStandardOutput 
 21  from .CommonServer import CommonServer 
 22  from .CommonVCSCommit import CommonVCSCommit 
 23   
 24  from PyFoam.ThirdParty.six import print_ 
 25   
 26  from os import path,listdir,symlink 
 27  from glob import glob 
 28   
 29  import string 
 30   
31 -class Decomposer(PyFoamApplication, 32 CommonStandardOutput, 33 CommonServer, 34 CommonMultiRegion, 35 CommonVCSCommit):
36 - def __init__(self, 37 args=None, 38 **kwargs):
39 description="""\ 40 Generates a decomposeParDict for a case and runs the decompose-Utility 41 on that case 42 """ 43 PyFoamApplication.__init__(self, 44 args=args, 45 description=description, 46 usage="%prog [options] <case> <procnr>", 47 interspersed=True, 48 nr=2, 49 **kwargs)
50 51 decomposeChoices=["metis","simple","hierarchical","manual"] 52 defaultMethod="metis" 53
54 - def addOptions(self):
55 if foamVersion()>=(1,6): 56 self.defaultMethod="scotch" 57 self.decomposeChoices+=[self.defaultMethod] 58 self.decomposeChoices+=["parMetis"] 59 60 spec=OptionGroup(self.parser, 61 "Decomposition Specification", 62 "How the case should be decomposed") 63 spec.add_option("--method", 64 type="choice", 65 default=self.defaultMethod, 66 dest="method", 67 action="store", 68 choices=self.decomposeChoices, 69 help="The method used for decomposing (Choices: "+", ".join(self.decomposeChoices)+") Default: %default") 70 71 spec.add_option("--n", 72 dest="n", 73 action="store", 74 default=None, 75 help="Number of subdivisions in coordinate directions. A python list or tuple (for simple and hierarchical)") 76 77 spec.add_option("--delta", 78 dest="delta", 79 action="store", 80 type="float", 81 default=None, 82 help="Cell skew factor (for simple and hierarchical)") 83 84 spec.add_option("--order", 85 dest="order", 86 action="store", 87 default=None, 88 help="Order of decomposition (for hierarchical)") 89 90 spec.add_option("--processorWeights", 91 dest="processorWeights", 92 action="store", 93 default=None, 94 help="The weights of the processors. A python list. Used for metis, scotch and parMetis") 95 96 spec.add_option("--globalFaceZones", 97 dest="globalFaceZones", 98 action="store", 99 default=None, 100 help="""Global face zones. A string with a python list or an OpenFOAM-list of words. Used for the GGI interface. Ex: '["GGI_Z1","GGI_Z2"]' or '(GGI_Z1 GGI_Z2)'""") 101 102 spec.add_option("--dataFile", 103 dest="dataFile", 104 action="store", 105 default=None, 106 help="File with the allocations. (for manual)") 107 self.parser.add_option_group(spec) 108 109 behave=OptionGroup(self.parser, 110 "Decomposition behaviour", 111 "How the program should behave during decomposition") 112 behave.add_option("--test", 113 dest="test", 114 action="store_true", 115 default=False, 116 help="Just print the resulting dictionary") 117 118 behave.add_option("--clear", 119 dest="clear", 120 action="store_true", 121 default=False, 122 help="Clear the case of previous processor directories") 123 124 behave.add_option("--no-decompose", 125 dest="doDecompose", 126 action="store_false", 127 default=True, 128 help="Don't run the decomposer (only writes the dictionary") 129 130 behave.add_option("--do-function-objects", 131 dest="doFunctionObjects", 132 action="store_true", 133 default=False, 134 help="Allow the execution of function objects (default behaviour is switching them off)") 135 136 behave.add_option("--decomposer", 137 dest="decomposer", 138 action="store", 139 default="decomposePar", 140 help="The decompose Utility that should be used") 141 self.parser.add_option_group(behave) 142 143 work=OptionGroup(self.parser, 144 "Additional work", 145 "What else should be done in addition to decomposing") 146 work.add_option("--constant-link", 147 dest="doConstantLinks", 148 action="store_true", 149 default=False, 150 help="Add links to the contents of the constant directory to the constant directories of the processor-directories") 151 self.parser.add_option_group(work) 152 153 CommonMultiRegion.addOptions(self) 154 CommonStandardOutput.addOptions(self) 155 CommonServer.addOptions(self,False) 156 CommonVCSCommit.addOptions(self)
157
158 - def run(self):
159 decomposeParWithRegion=(foamVersion()>=(1,6)) 160 161 if self.opts.keeppseudo and (not self.opts.regions and self.opts.region==None): 162 warning("Option --keep-pseudocases only makes sense for multi-region-cases") 163 164 if decomposeParWithRegion and self.opts.keeppseudo: 165 warning("Option --keep-pseudocases doesn't make sense since OpenFOAM 1.6 because decomposePar supports regions") 166 167 nr=int(self.parser.getArgs()[1]) 168 if nr<2: 169 error("Number of processors",nr,"too small (at least 2)") 170 171 case=path.abspath(self.parser.getArgs()[0]) 172 method=self.opts.method 173 174 result={} 175 result["numberOfSubdomains"]=nr 176 result["method"]=method 177 178 coeff={} 179 result[method+"Coeffs"]=coeff 180 181 if self.opts.globalFaceZones!=None: 182 try: 183 fZones=eval(self.opts.globalFaceZones) 184 except SyntaxError: 185 fZones=FoamStringParser( 186 self.opts.globalFaceZones, 187 listDict=True 188 ).data 189 190 result["globalFaceZones"]=fZones 191 192 if method in ["metis","scotch","parMetis"]: 193 if self.opts.processorWeights!=None: 194 weigh=eval(self.opts.processorWeights) 195 if nr!=len(weigh): 196 error("Number of processors",nr,"and length of",weigh,"differ") 197 coeff["processorWeights"]=weigh 198 elif method=="manual": 199 if self.opts.dataFile==None: 200 error("Missing required option dataFile") 201 else: 202 coeff["dataFile"]="\""+self.opts.dataFile+"\"" 203 elif method=="simple" or method=="hierarchical": 204 if self.opts.n==None or self.opts.delta==None: 205 error("Missing required option n or delta") 206 n=eval(self.opts.n) 207 if len(n)!=3: 208 error("Needs to be three elements, not",n) 209 if nr!=n[0]*n[1]*n[2]: 210 error("Subdomains",n,"inconsistent with processor number",nr) 211 coeff["n"]="(%d %d %d)" % (n[0],n[1],n[2]) 212 213 coeff["delta"]=float(self.opts.delta) 214 if method=="hierarchical": 215 if self.opts.order==None: 216 error("Missing reuired option order") 217 if len(self.opts.order)!=3: 218 error("Order needs to be three characters") 219 coeff["order"]=self.opts.order 220 else: 221 error("Method",method,"not yet implementes") 222 223 gen=FoamFileGenerator(result) 224 225 if self.opts.test: 226 print_(str(gen)) 227 return -1 228 else: 229 f=open(path.join(case,"system","decomposeParDict"),"w") 230 writeDictionaryHeader(f) 231 f.write(str(gen)) 232 f.close() 233 234 if self.opts.clear: 235 print_("Clearing processors") 236 for p in glob(path.join(case,"processor*")): 237 print_("Removing",p) 238 rmtree(p,ignore_errors=True) 239 240 self.checkAndCommit(SolutionDirectory(case,archive=None)) 241 242 if self.opts.doDecompose: 243 if self.opts.region: 244 regionNames=self.opts.region[:] 245 while True: 246 try: 247 i=regionNames.index("region0") 248 regionNames[i]=None 249 except ValueError: 250 break 251 else: 252 regionNames=[None] 253 254 regions=None 255 256 sol=SolutionDirectory(case) 257 if not decomposeParWithRegion: 258 if self.opts.regions or self.opts.region!=None: 259 print_("Building Pseudocases") 260 regions=RegionCases(sol,clean=True,processorDirs=False) 261 262 if self.opts.regions: 263 regionNames=sol.getRegions(defaultRegion=True) 264 265 for theRegion in regionNames: 266 theCase=path.normpath(case) 267 if theRegion!=None and not decomposeParWithRegion: 268 theCase+="."+theRegion 269 270 if oldApp(): 271 argv=[self.opts.decomposer,".",theCase] 272 else: 273 argv=[self.opts.decomposer,"-case",theCase] 274 if foamVersion()>=(2,0) and not self.opts.doFunctionObjects: 275 argv+=["-noFunctionObjects"] 276 if theRegion!=None and decomposeParWithRegion: 277 argv+=["-region",theRegion] 278 279 f=open(path.join(case,"system",theRegion,"decomposeParDict"),"w") 280 writeDictionaryHeader(f) 281 f.write(str(gen)) 282 f.close() 283 284 self.setLogname(default="Decomposer",useApplication=False) 285 286 run=UtilityRunner(argv=argv, 287 silent=self.opts.progress or self.opts.silent, 288 logname=self.opts.logname, 289 compressLog=self.opts.compress, 290 server=self.opts.server, 291 noLog=self.opts.noLog, 292 logTail=self.opts.logTail, 293 echoCommandLine=self.opts.echoCommandPrefix, 294 jobId=self.opts.jobId) 295 run.start() 296 297 if theRegion!=None and not decomposeParWithRegion: 298 print_("Syncing into master case") 299 regions.resync(theRegion) 300 301 if regions!=None and not decomposeParWithRegion: 302 if not self.opts.keeppseudo: 303 print_("Removing pseudo-regions") 304 regions.cleanAll() 305 else: 306 for r in sol.getRegions(): 307 if r not in regionNames: 308 regions.clean(r) 309 310 if self.opts.doConstantLinks: 311 print_("Adding symlinks in the constant directories") 312 constPath=path.join(case,"constant") 313 for f in listdir(constPath): 314 srcExpr=path.join(path.pardir,path.pardir,"constant",f) 315 for p in range(nr): 316 dest=path.join(case,"processor%d"%p,"constant",f) 317 if not path.exists(dest): 318 symlink(srcExpr,dest) 319 320 self.addToCaseLog(case)
321 322 # Should work with Python3 and Python2 323