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,listdir,symlink
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 work=OptionGroup(self.parser,
126 "Additional work",
127 "What else should be done in addition to decomposing")
128 work.add_option("--constant-link",
129 dest="doConstantLinks",
130 action="store_true",
131 default=False,
132 help="Add links to the contents of the constant directory to the constant directories of the processor-directories")
133 self.parser.add_option_group(work)
134
135 CommonMultiRegion.addOptions(self)
136 CommonStandardOutput.addOptions(self)
137 CommonServer.addOptions(self,False)
138
140 if self.opts.keeppseudo and (not self.opts.regions and self.opts.region==None):
141 warning("Option --keep-pseudocases only makes sense for multi-region-cases")
142
143 nr=int(self.parser.getArgs()[1])
144 if nr<2:
145 error("Number of processors",nr,"too small (at least 2)")
146
147 case=path.abspath(self.parser.getArgs()[0])
148 method=self.opts.method
149
150 result={}
151 result["numberOfSubdomains"]=nr
152 result["method"]=method
153
154 coeff={}
155 result[method+"Coeffs"]=coeff
156
157 if self.opts.globalFaceZones!=None:
158 fZones=eval(self.opts.globalFaceZones)
159 result["globalFaceZones"]=fZones
160
161 if method=="metis" or method=="scotch":
162 if self.opts.processorWeights!=None:
163 weigh=eval(self.opts.processorWeights)
164 if nr!=len(weigh):
165 error("Number of processors",nr,"and length of",weigh,"differ")
166 coeff["processorWeights"]=weigh
167 elif method=="manual":
168 if self.opts.dataFile==None:
169 error("Missing required option dataFile")
170 else:
171 coeff["dataFile"]="\""+self.opts.dataFile+"\""
172 elif method=="simple" or method=="hierarchical":
173 if self.opts.n==None or self.opts.delta==None:
174 error("Missing required option n or delta")
175 n=eval(self.opts.n)
176 if len(n)!=3:
177 error("Needs to be three elements, not",n)
178 if nr!=n[0]*n[1]*n[2]:
179 error("Subdomains",n,"inconsistent with processor number",nr)
180 coeff["n"]="(%d %d %d)" % (n[0],n[1],n[2])
181
182 coeff["delta"]=float(self.opts.delta)
183 if method=="hierarchical":
184 if self.opts.order==None:
185 error("Missing reuired option order")
186 if len(self.opts.order)!=3:
187 error("Order needs to be three characters")
188 coeff["order"]=self.opts.order
189 else:
190 error("Method",method,"not yet implementes")
191
192 gen=FoamFileGenerator(result)
193
194 if self.opts.test:
195 print str(gen)
196 sys.exit(-1)
197 else:
198 f=open(path.join(case,"system","decomposeParDict"),"w")
199 writeDictionaryHeader(f)
200 f.write(str(gen))
201 f.close()
202
203 if self.opts.clear:
204 system("rm -rf "+path.join(case,"processor*"))
205
206 if self.opts.doDecompose:
207 regionNames=[self.opts.region]
208 regions=None
209
210 if self.opts.regions or self.opts.region!=None:
211 print "Building Pseudocases"
212 sol=SolutionDirectory(case)
213 regions=RegionCases(sol,clean=True,processorDirs=False)
214
215 if self.opts.regions:
216 regionNames=sol.getRegions()
217
218 for theRegion in regionNames:
219 theCase=path.normpath(case)
220 if theRegion!=None:
221 theCase+="."+theRegion
222
223 if oldApp():
224 argv=[self.opts.decomposer,".",theCase]
225 else:
226 argv=[self.opts.decomposer,"-case",theCase]
227
228 self.setLogname(default="Decomposer",useApplication=False)
229
230 run=UtilityRunner(argv=argv,
231 silent=self.opts.progress,
232 logname=self.opts.logname,
233 compressLog=self.opts.compress,
234 server=self.opts.server,
235 noLog=self.opts.noLog,
236 jobId=self.opts.jobId)
237 run.start()
238
239 if theRegion!=None:
240 print "Syncing into master case"
241 regions.resync(theRegion)
242
243 if regions!=None:
244 if not self.opts.keeppseudo:
245 print "Removing pseudo-regions"
246 regions.cleanAll()
247 else:
248 for r in sol.getRegions():
249 if r not in regionNames:
250 regions.clean(r)
251
252 if self.opts.doConstantLinks:
253 print "Adding symlinks in the constant directories"
254 constPath=path.join(case,"constant")
255 for f in listdir(constPath):
256 srcExpr=path.join(path.pardir,path.pardir,"constant",f)
257 for p in range(nr):
258 dest=path.join(case,"processor%d"%p,"constant",f)
259 if not path.exists(dest):
260 symlink(srcExpr,dest)
261
262 self.addToCaseLog(case)
263