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