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