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