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