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