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