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