1
2 """
3 Class that implements pyFoamBenchmark
4 """
5
6 from PyFoamApplication import PyFoamApplication
7
8 from fnmatch import fnmatch
9
10 import sys,string,ConfigParser
11
12 from os import path,uname
13 from time import time,localtime,asctime
14 from PyFoam.Execution.BasicRunner import BasicRunner
15 from PyFoam.FoamInformation import foamTutorials
16 from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
17 from PyFoam.RunDictionary.SolutionFile import SolutionFile
18 from PyFoam.RunDictionary.ParameterFile import ParameterFile
19 from PyFoam.RunDictionary.BlockMesh import BlockMesh
20 from PyFoam.Execution.ParallelExecution import LAMMachine
21 from PyFoam.Basics.Utilities import execute
22 from PyFoam.Basics.CSVCollection import CSVCollection
23 from PyFoam.FoamInformation import oldAppConvention as oldApp
24
31
33 self.parser.add_option("--nameAddition",
34 action="store",
35 dest="nameAddition",
36 default=None,
37 help="Addition to the name that helps to distinguish different runs of the same configuration")
38 self.parser.add_option("--removeCases",
39 action="store_true",
40 dest="removeCases",
41 default=False,
42 help="Remove the case directories and log files for all successfully run cases")
43 self.parser.add_option("--exclude-cases",
44 action="append",
45 default=None,
46 dest="excases",
47 help="Cases which should not be processed (pattern, can be used more than once)")
48 self.parser.add_option("--cases",
49 action="append",
50 default=None,
51 dest="cases",
52 help="Cases which should be processed (pattern, can be used more than once)")
53
55 config=ConfigParser.ConfigParser()
56 files=self.parser.getArgs()
57
58 good=config.read(files)
59
60
61
62
63
64
65 benchName=config.get("General","name")
66 if self.opts.nameAddition!=None:
67 benchName+="_"+self.opts.nameAddition
68 if self.opts.foamVersion!=None:
69 benchName+="_v"+self.opts.foamVersion
70
71 isParallel=config.getboolean("General","parallel")
72 lam=None
73
74 if isParallel:
75 nrCpus=config.getint("General","nProcs")
76 machineFile=config.get("General","machines")
77 if not path.exists(machineFile):
78 print "Machine file ",machineFile,"needed for parallel run"
79 sys.exit(-1)
80 lam=LAMMachine(machineFile,nr=nrCpus)
81 if lam.cpuNr()>nrCpus:
82 print "Wrong number of CPUs: ",lam.cpuNr()
83 sys.exit(-1)
84
85 print "Running parallel on",lam.cpuNr(),"CPUs"
86
87 if config.has_option("General","casesDirectory"):
88 casesDirectory=path.expanduser(config.get("General","casesDirectory"))
89 else:
90 casesDirectory=foamTutorials()
91
92 if not path.exists(casesDirectory):
93 print "Directory",casesDirectory,"needed with the benchmark cases is missing"
94 sys.exit(-1)
95 else:
96 print "Using cases from directory",casesDirectory
97
98 benchCases=[]
99 config.remove_section("General")
100
101 for sec in config.sections():
102 print "Reading: ",sec
103 skipIt=False
104 skipReason=""
105 if config.has_option(sec,"skip"):
106 skipIt=config.getboolean(sec,"skip")
107 skipReason="Switched off in file"
108 if self.opts.excases!=None and not skipIt:
109 for p in self.opts.excases:
110 if fnmatch(sec,p):
111 skipIt=True
112 skipReason="Switched off by pattern '"+p+"'"
113 if self.opts.cases!=None:
114 for p in self.opts.cases:
115 if fnmatch(sec,p):
116 skipIt=False
117 skipReason=""
118
119 if skipIt:
120 print "Skipping case ..... Reason:"+skipReason
121 continue
122 sol=config.get(sec,"solver")
123 cas=config.get(sec,"case")
124 pre=eval(config.get(sec,"prepare"))
125 preCon=[]
126 if config.has_option(sec,"preControlDict"):
127 preCon=eval(config.get(sec,"preControlDict"))
128 con=eval(config.get(sec,"controlDict"))
129 bas=config.getfloat(sec,"baseline")
130 wei=config.getfloat(sec,"weight")
131 add=[]
132 if config.has_option(sec,"additional"):
133 add=eval(config.get(sec,"additional"))
134 print "Adding: ", add
135 util=[]
136 if config.has_option(sec,"utilities"):
137 util=eval(config.get(sec,"utilities"))
138 print "Utilities: ", util
139 nr=99999
140 if config.has_option(sec,"nr"):
141 nr=eval(config.get(sec,"nr"))
142 sp=None
143 if config.has_option(sec,"blockSplit"):
144 sp=eval(config.get(sec,"blockSplit"))
145 toRm=[]
146 if config.has_option(sec,"filesToRemove"):
147 toRm=eval(config.get(sec,"filesToRemove"))
148 setInit=[]
149 if config.has_option(sec,"setInitial"):
150 setInit=eval(config.get(sec,"setInitial"))
151
152 parallelOK=False
153 if config.has_option(sec,"parallelOK"):
154 parallelOK=config.getboolean(sec,"parallelOK")
155
156 deMet=["metis"]
157 if config.has_option(sec,"decomposition"):
158 deMet=config.get(sec,"decomposition").split()
159
160 if deMet[0]=="metis":
161 pass
162 elif deMet[0]=="simple":
163 if len(deMet)<2:
164 deMet.append(0)
165 else:
166 deMet[1]=int(deMet[1])
167 else:
168 print "Unimplemented decomposition method",deMet[0],"switching to metis"
169 deMet=["metis"]
170
171 if isParallel==False or parallelOK==True:
172 if path.exists(path.join(casesDirectory,sol,cas)):
173 benchCases.append( (nr,sec,sol,cas,pre,con,preCon,bas,wei,add,util,sp,toRm,setInit,deMet) )
174 else:
175 print "Skipping",sec,"because directory",path.join(casesDirectory,sol,cas),"could not be found"
176 else:
177 print "Skipping",sec,"because not parallel"
178
179 benchCases.sort()
180
181 parallelString=""
182 if isParallel:
183 parallelString=".cpus="+str(nrCpus)
184
185 resultFile=open("Benchmark."+benchName+"."+uname()[1]+parallelString+".results","w")
186
187 totalSpeedup=0
188 minSpeedup=None
189 maxSpeedup=None
190 totalWeight =0
191 runsOK=0
192 currentEstimate = 1.
193
194 print "\nStart Benching\n"
195
196 csv=CSVCollection("Benchmark."+benchName+"."+uname()[1]+parallelString+".csv")
197
198
199
200
201
202 for nr,description,solver,case,prepare,control,preControl,base,weight,additional,utilities,split,toRemove,setInit,decomposition in benchCases:
203
204 print "Running Benchmark: ",description
205 print "Solver: ",solver
206 print "Case: ",case
207 caseName=solver+"_"+case+"_"+benchName+"."+uname()[1]+".case"
208 print "Short name: ",caseName
209 caseDir=caseName+".runDir"
210
211 csv["description"]=description
212 csv["solver"]=solver
213 csv["case"]=case
214 csv["caseDir"]=caseDir
215 csv["base"]=base
216
217 csv["benchmark"]=benchName
218 csv["machine"]=uname()[1]
219 csv["arch"]=uname()[4]
220 if lam==None:
221 csv["cpus"]=1
222 else:
223 csv["cpus"]=lam.cpuNr()
224 csv["os"]=uname()[0]
225 csv["version"]=uname()[2]
226
227 workDir=path.realpath(path.curdir)
228
229 orig=SolutionDirectory(path.join(casesDirectory,solver,case),
230 archive=None,
231 paraviewLink=False)
232 for a in additional+utilities:
233 orig.addToClone(a)
234 orig.cloneCase(path.join(workDir,caseDir))
235
236 if oldApp():
237 argv=[solver,workDir,caseDir]
238 else:
239 argv=[solver,"-case",path.join(workDir,caseDir)]
240
241 run=BasicRunner(silent=True,argv=argv,logname="BenchRunning",lam=lam)
242 runDir=run.getSolutionDirectory()
243 controlFile=ParameterFile(runDir.controlDict())
244
245 for name,value in preControl:
246 print "Setting parameter",name,"to",value,"in controlDict"
247 controlFile.replaceParameter(name,value)
248
249 for rm in toRemove:
250 fn=path.join(caseDir,rm)
251 print "Removing file",fn
252 execute("rm -f "+fn)
253
254 for field,bc,val in setInit:
255 print "Setting",field,"on",bc,"to",val
256 SolutionFile(runDir.initialDir(),field).replaceBoundary(bc,val)
257
258 oldDeltaT=controlFile.replaceParameter("deltaT",0)
259
260 for u in utilities:
261 print "Building utility ",u
262 execute("wmake 2>&1 >%s %s" % (path.join(caseDir,"BenchCompile."+u),path.join(caseDir,u)))
263
264 print "Preparing the case: "
265 if lam!=None:
266 prepare=prepare+[("decomposePar","")]
267 if decomposition[0]=="metis":
268 lam.writeMetis(SolutionDirectory(path.join(workDir,caseDir)))
269 elif decomposition[0]=="simple":
270 lam.writeSimple(SolutionDirectory(path.join(workDir,caseDir)),decomposition[1])
271
272 if split:
273 print "Splitting the mesh:",split
274 bm=BlockMesh(runDir.blockMesh())
275 bm.refineMesh(split)
276
277 for pre,post in prepare:
278 print "Doing ",pre," ...."
279 post=post.replace("%case%",caseDir)
280 if oldApp():
281 args=string.split("%s %s %s %s" % (pre,workDir,caseDir,post))
282 else:
283 args=string.split("%s -case %s %s" % (pre,path.join(workDir,caseDir),post))
284 util=BasicRunner(silent=True,argv=args,logname="BenchPrepare_"+pre)
285 util.start()
286
287 controlFile.replaceParameter("deltaT",oldDeltaT)
288
289
290 for name,value in control:
291 print "Setting parameter",name,"to",value,"in controlDict"
292 controlFile.replaceParameter(name,value)
293
294 print "Starting at ",asctime(localtime(time()))
295 print " Baseline is %f, estimated speedup %f -> estimated end at %s " % (base,currentEstimate,asctime(localtime(time()+base/currentEstimate)))
296 print "Running the case ...."
297 run.start()
298
299 speedup=None
300 cpuUsage=0
301 speedupOut=-1
302
303 try:
304 speedup=base/run.run.wallTime()
305 cpuUsage=100.*run.run.cpuTime()/run.run.wallTime()
306 except ZeroDivisionError:
307 print "Division by Zero: ",run.run.wallTime()
308
309 if not run.runOK():
310 print "\nWARNING!!!!"
311 print "Run had a problem, not using the results. Check the log\n"
312 speedup=None
313
314 if speedup!=None:
315 speedupOut=speedup
316
317 totalSpeedup+=speedup*weight
318 totalWeight +=weight
319 runsOK+=1
320 if maxSpeedup==None:
321 maxSpeedup=speedup
322 elif speedup>maxSpeedup:
323 maxSpeedup=speedup
324 if minSpeedup==None:
325 minSpeedup=speedup
326 elif speedup<minSpeedup:
327 minSpeedup=speedup
328
329 print "Wall clock: ",run.run.wallTime()
330 print "Speedup: ",speedup," (Baseline: ",base,")"
331 print "CPU Time: ",run.run.cpuTime()
332 print "CPU Time User: ",run.run.cpuUserTime()
333 print "CPU Time System: ",run.run.cpuSystemTime()
334 print "Memory: ",run.run.usedMemory()
335 print "CPU Usage: %6.2f%%" % (cpuUsage)
336
337 csv["wallclocktime"]=run.run.wallTime()
338 csv["cputime"]=run.run.cpuTime()
339 csv["cputimeuser"]=run.run.cpuUserTime()
340 csv["cputimesystem"]=run.run.cpuSystemTime()
341 csv["maxmemory"]=run.run.usedMemory()
342 csv["cpuusage"]=cpuUsage
343 if speedup!=None:
344 csv["speedup"]=speedup
345 else:
346 csv["speedup"]="##"
347
348 csv.write()
349
350 resultFile.write("Case %s WallTime %g CPUTime %g UserTime %g SystemTime %g Memory %g MB Speedup %g\n" %(caseName,run.run.wallTime(),run.run.cpuTime(),run.run.cpuUserTime(),run.run.cpuSystemTime(),run.run.usedMemory(),speedupOut))
351
352 resultFile.flush()
353
354 if speedup!=None:
355 currentEstimate=totalSpeedup/totalWeight
356
357 if self.opts.removeCases:
358 print "Clearing case",
359 if speedup==None:
360 print "not ... because it failed"
361 else:
362 print "completely"
363 execute("rm -rf "+caseDir)
364
365 print
366 print
367
368 if lam!=None:
369 lam.stop()
370
371 print "Total Speedup: ",currentEstimate," ( ",totalSpeedup," / ",totalWeight, " ) Range: [",minSpeedup,",",maxSpeedup,"]"
372
373 print runsOK,"of",len(benchCases),"ran OK"
374
375 resultFile.write("Total Speedup: %g\n" % (currentEstimate))
376 if minSpeedup and maxSpeedup:
377 resultFile.write("Range: [ %g , %g ]\n" % (minSpeedup,maxSpeedup))
378
379 resultFile.close()
380