1
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
12 from PyFoam.Execution.UtilityRunner import UtilityRunner
13 from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
14 from PyFoam.RunDictionary.RegionCases import RegionCases
15 from PyFoam.FoamInformation import oldAppConvention as oldApp
16 from PyFoam.FoamInformation import foamVersion
17
18 from CommonMultiRegion import CommonMultiRegion
19 from CommonStandardOutput import CommonStandardOutput
20 from CommonServer import CommonServer
21
22 from os import path,system
23 import sys,string
24
25 -class Decomposer(PyFoamApplication,
26 CommonStandardOutput,
27 CommonServer,
28 CommonMultiRegion):
39
40 decomposeChoices=["metis","simple","hierarchical","manual"]
41 defaultMethod="metis"
42
44 if foamVersion()>=(1,6):
45 self.defaultMethod="scotch"
46 self.decomposeChoices+=[self.defaultMethod]
47
48 spec=OptionGroup(self.parser,
49 "Decomposition Specification",
50 "How the case should be decomposed")
51 spec.add_option("--method",
52 type="choice",
53 default=self.defaultMethod,
54 dest="method",
55 action="store",
56 choices=self.decomposeChoices,
57 help="The method used for decomposing (Choices: "+string.join(self.decomposeChoices,", ")+") Default: %default")
58
59 spec.add_option("--n",
60 dest="n",
61 action="store",
62 default=None,
63 help="Number of subdivisions in coordinate directions. A python list or tuple (for simple and hierarchical)")
64
65 spec.add_option("--delta",
66 dest="delta",
67 action="store",
68 type="float",
69 default=None,
70 help="Cell skew factor (for simple and hierarchical)")
71
72 spec.add_option("--order",
73 dest="order",
74 action="store",
75 default=None,
76 help="Order of decomposition (for hierarchical)")
77
78 spec.add_option("--processorWeights",
79 dest="processorWeights",
80 action="store",
81 default=None,
82 help="The weights of the processors. A python list. Used for metis")
83
84 spec.add_option("--globalFaceZones",
85 dest="globalFaceZones",
86 action="store",
87 default=None,
88 help="Global face zones. A python string. Used for the GGI interface. Ex: '(GGI_Z1 GGI_Z2)'")
89
90 spec.add_option("--dataFile",
91 dest="dataFile",
92 action="store",
93 default=None,
94 help="File with the allocations. (for manual)")
95 self.parser.add_option_group(spec)
96
97 behave=OptionGroup(self.parser,
98 "Decomposition behaviour",
99 "How the program should behave during decomposition")
100 behave.add_option("--test",
101 dest="test",
102 action="store_true",
103 default=False,
104 help="Just print the resulting dictionary")
105
106 behave.add_option("--clear",
107 dest="clear",
108 action="store_true",
109 default=False,
110 help="Clear the case of previous processor directories")
111
112 behave.add_option("--no-decompose",
113 dest="doDecompose",
114 action="store_false",
115 default=True,
116 help="Don't run the decomposer (only writes the dictionary")
117
118 behave.add_option("--decomposer",
119 dest="decomposer",
120 action="store",
121 default="decomposePar",
122 help="The decompose Utility that should be used")
123 self.parser.add_option_group(behave)
124
125 CommonMultiRegion.addOptions(self)
126 CommonStandardOutput.addOptions(self)
127 CommonServer.addOptions(self,False)
128
130 if self.opts.keeppseudo and (not self.opts.regions and self.opts.region==None):
131 warning("Option --keep-pseudocases only makes sense for multi-region-cases")
132
133 nr=int(self.parser.getArgs()[1])
134 if nr<2:
135 error("Number of processors",nr,"too small (at least 2)")
136
137 case=self.parser.getArgs()[0]
138 method=self.opts.method
139
140 result={}
141 result["numberOfSubdomains"]=nr
142 result["method"]=method
143
144 coeff={}
145 result[method+"Coeffs"]=coeff
146
147 if self.opts.globalFaceZones!=None:
148 fZones=eval(self.opts.globalFaceZones)
149 result["globalFaceZones"]=fZones
150
151 if method=="metis" or method=="scotch":
152 if self.opts.processorWeights!=None:
153 weigh=eval(self.opts.processorWeights)
154 if nr!=len(weigh):
155 error("Number of processors",nr,"and length of",weigh,"differ")
156 coeff["processorWeights"]=weigh
157 elif method=="manual":
158 if self.opts.dataFile==None:
159 error("Missing required option dataFile")
160 else:
161 coeff["dataFile"]="\""+self.opts.dataFile+"\""
162 elif method=="simple" or method=="hierarchical":
163 if self.opts.n==None or self.opts.delta==None:
164 error("Missing required option n or delta")
165 n=eval(self.opts.n)
166 if len(n)!=3:
167 error("Needs to be three elements, not",n)
168 if nr!=n[0]*n[1]*n[2]:
169 error("Subdomains",n,"inconsistent with processor number",nr)
170 coeff["n"]="(%d %d %d)" % (n[0],n[1],n[2])
171
172 coeff["delta"]=float(self.opts.delta)
173 if method=="hierarchical":
174 if self.opts.order==None:
175 error("Missing reuired option order")
176 if len(self.opts.order)!=3:
177 error("Order needs to be three characters")
178 coeff["order"]=self.opts.order
179 else:
180 error("Method",method,"not yet implementes")
181
182 gen=FoamFileGenerator(result)
183
184 if self.opts.test:
185 print str(gen)
186 sys.exit(-1)
187 else:
188 f=open(path.join(case,"system","decomposeParDict"),"w")
189 writeDictionaryHeader(f)
190 f.write(str(gen))
191 f.close()
192
193 if self.opts.clear:
194 system("rm -rf "+path.join(case,"processor*"))
195
196 if self.opts.doDecompose:
197 regionNames=[self.opts.region]
198 regions=None
199
200 if self.opts.regions or self.opts.region!=None:
201 print "Building Pseudocases"
202 sol=SolutionDirectory(case)
203 regions=RegionCases(sol,clean=True)
204
205 if self.opts.regions:
206 regionNames=sol.getRegions()
207
208 for theRegion in regionNames:
209 theCase=path.normpath(case)
210 if theRegion!=None:
211 theCase+="."+theRegion
212
213 if oldApp():
214 argv=[self.opts.decomposer,".",theCase]
215 else:
216 argv=[self.opts.decomposer,"-case",theCase]
217
218 self.setLogname(default="Decomposer",useApplication=False)
219
220 run=UtilityRunner(argv=argv,
221 silent=self.opts.progress,
222 logname=self.opts.logname,
223 compressLog=self.opts.compress,
224 server=self.opts.server,
225 noLog=self.opts.noLog,
226 jobId=self.opts.jobId)
227 run.start()
228
229 if theRegion!=None:
230 print "Syncing into master case"
231 regions.resync(theRegion)
232
233 if regions!=None:
234 if not self.opts.keeppseudo:
235 print "Removing pseudo-regions"
236 regions.cleanAll()
237 else:
238 for r in sol.getRegions():
239 if r not in regionNames:
240 regions.clean(r)
241
242 self.addToCaseLog(case)
243