1
2 """
3 Application class that implements pyFoamPrepareCase
4 """
5
6 from optparse import OptionGroup
7 from os import path
8
9 from .PrepareCase import PrepareCase
10
11 from PyFoam.Execution.AnalyzedRunner import AnalyzedRunner
12 from PyFoam.LogAnalysis.BoundingLogAnalyzer import BoundingLogAnalyzer
13 from PyFoam.RunDictionary.SolutionDirectory import SolutionDirectory
14 from PyFoam.RunDictionary.RegionCases import RegionCases
15 from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile
16 try:
17 from PyFoam.Basics.RunDatabase import RunDatabase
18 hasDatabase=True
19 except ImportError:
20 hasDatabase=False
21
22 from PyFoam.Error import warning
23
24 from .CommonPlotLines import CommonPlotLines
25 from .CommonReportUsage import CommonReportUsage
26 from .CommonReportRunnerData import CommonReportRunnerData
27 from .CommonStandardOutput import CommonStandardOutput
28 from .CommonParallel import CommonParallel
29 from .CommonServer import CommonServer
30 from .CommonPrePostHooks import CommonPrePostHooks
31
32 from PyFoam.Basics.CustomPlotInfo import resetCustomCounter
33 from PyFoam.Basics.TimeLineCollection import allLines
34 from PyFoam.Basics.GeneralPlotTimelines import allPlots
35
36 from PyFoam.ThirdParty.six import print_
37
38 -class RunParameterVariation(PrepareCase,
39 CommonPlotLines,
40 CommonReportUsage,
41 CommonReportRunnerData,
42 CommonParallel,
43 CommonServer,
44 CommonStandardOutput,
45 CommonPrePostHooks):
46 - def __init__(self,
47 args=None,
48 **kwargs):
49 description="""\
50 Takes a template case and a file that specifies the parameters to be varied.
51 Using the machinery from pyFoamPrepareCase.py it sets up the case with each
52 parameters and runs the solver on it. Collects the results in a database
53 """
54
55 examples="""\
56 Do all the variations in the template case:
57
58 %prog --inplace-execution templateCase parameter.file
59
60 One case for every variation and start with the 4th variant
61
62 %prog --every-variant-one-case-execution templateCase parameter.file --start=4
63 """
64 CommonPlotLines.__init__(self)
65 PrepareCase.__init__(self,
66 nr=2,
67 usage="%prog <case> <parameterFile>",
68 exactNr=True,
69 args=args,
70 interspersed=True,
71 description=description,
72 examples=examples,
73 **kwargs)
74
76 CommonReportUsage.addOptions(self)
77 CommonReportRunnerData.addOptions(self)
78 CommonStandardOutput.addOptions(self)
79 CommonParallel.addOptions(self)
80 CommonPlotLines.addOptions(self)
81 CommonServer.addOptions(self)
82 CommonPrePostHooks.addOptions(self)
83
84 PrepareCase.addOptions(self)
85
86 variation=OptionGroup(self.parser,
87 "Parameter variation",
88 "Parameters specific to the parameter variation")
89 self.parser.add_option_group(variation)
90
91 variation.add_option("--inplace-execution",
92 action="store_true",
93 dest="inplaceExecution",
94 default=False,
95 help="Do everything in the template case (preparation and execution)")
96 variation.add_option("--one-cloned-case-execution",
97 action="store_true",
98 dest="oneClonedCase",
99 default=False,
100 help="Clone to one case and do everything in that case (preparation and execution)")
101 variation.add_option("--every-variant-one-case-execution",
102 action="store_true",
103 dest="everyVariantOneCase",
104 default=False,
105 help="Every variation gets its own case (that is not erased)")
106 variation.add_option("--no-execute-solver",
107 action="store_true",
108 dest="noExecuteSolver",
109 default=False,
110 help="Only prepare the cases but do not execute the solver")
111 variation.add_option("--cloned-case-prefix",
112 action="store",
113 dest="clonedCasePrefix",
114 default=None,
115 help="Prefix of the cloned cases. If unspecified the name of parameter file is used")
116 variation.add_option("--clone-to-directory",
117 action="store",
118 dest="cloneToDirectory",
119 default=None,
120 help="Directory to clone to. If unspecified use the directory in which the original case resides")
121 variation.add_option("--single-variation",
122 action="store",
123 type="int",
124 dest="singleVariation",
125 default=None,
126 help="Single variation to run")
127 variation.add_option("--start-variation-number",
128 action="store",
129 type="int",
130 dest="startVariation",
131 default=None,
132 help="Variation number to start with")
133 variation.add_option("--end-variation-number",
134 action="store",
135 type="int",
136 dest="endVariation",
137 default=None,
138 help="Variation number to end with")
139 variation.add_option("--database",
140 action="store",
141 dest="database",
142 default=None,
143 help="Path to the database. If unset the name of parameter-file appended with '.results' will be used")
144 variation.add_option("--create-database",
145 action="store_true",
146 dest="createDatabase",
147 default=False,
148 help="Create a new database file. Fail if it already exists")
149 variation.add_option("--auto-create-database",
150 action="store_true",
151 dest="autoCreateDatabase",
152 default=False,
153 help="Create a new database file if it doesn't exist yet'")
154 variation.add_option("--list-variations",
155 action="store_true",
156 dest="listVariations",
157 default=False,
158 help="List the selected variations but don't do anything")
159 variation.add_option("--no-database-write",
160 action="store_true",
161 dest="noDatabaseWrite",
162 default=False,
163 help="Do not write to the database")
164
172
174 origPath=self.parser.getArgs()[0]
175 parameterFile=self.parser.getArgs()[1]
176
177 self.addLocalConfig(origPath)
178 self.checkCase(origPath)
179
180 nrModes=(1 if self.opts.inplaceExecution else 0) + \
181 (1 if self.opts.oneClonedCase else 0) + \
182 (1 if self.opts.listVariations else 0) + \
183 (1 if self.opts.everyVariantOneCase else 0)
184 if nrModes==0:
185 self.error("Specify one of the modes --list-variations, --inplace-execution, --one-cloned-case-execution or --every-variant-one-case-execution")
186 elif nrModes>1:
187 self.error("The modes --list-variations, --inplace-execution, --one-cloned-case-execution or --every-variant-one-case-execution are mutual exclusive")
188 if self.opts.noExecuteSolver:
189 if not self.opts.everyVariantOneCase and self.opts.singleVariation==None and not self.opts.listVariations:
190 self.error("--no-execute-solver only works with --every-variant-one-case-execution")
191
192 if not self.opts.clonedCasePrefix:
193 self.opts.clonedCasePrefix=path.basename(parameterFile)
194 if not self.opts.cloneToDirectory:
195 self.opts.cloneToDirectory=path.dirname(path.abspath(origPath))
196 if not self.opts.database:
197 self.opts.database=parameterFile+".database"
198
199 variationData=ParsedParameterFile(parameterFile,
200 noHeader=True,
201 noVectorOrTensor=True)
202 if not "values" in variationData:
203 self.error("Entry 'values' (dictionary) needed in",parameterFile)
204 if not "solver" in variationData["values"]:
205 self.error("Entry 'solver' (list or string) needed in 'values' in",parameterFile)
206
207 fixed={}
208 varied=[]
209 nrVariations=1
210
211 for k in variationData["values"]:
212 v=variationData["values"][k]
213 if type(v)!=list:
214 self.error("Entry",k,"is not a list")
215 if len(v)==1:
216 fixed[k]=v[0]
217 elif len(v)>1:
218 varied.append((k,v))
219 nrVariations*=len(v)
220 else:
221 self.warning("Entry",k,"is empty")
222
223 if len(varied)==0:
224 self.error("No parameters to vary")
225
226 self.printPhase(nrVariations,"variations with",len(varied),"parameters")
227
228 def makeVariations(vList):
229 name,vals=vList[0]
230 if len(vList)>1:
231 var=makeVariations(vList[1:])
232 variation=[]
233 for orig in var:
234 for v in vals:
235 d=orig.copy()
236 d[name]=v
237 variation.append(d)
238 return variation
239 else:
240 return [{name:v} for v in vals]
241
242 variations=makeVariations(varied)
243 self["variations"]=variations
244 self["fixed"]=fixed
245
246 if self.opts.startVariation!=None:
247 start=self.opts.startVariation
248 else:
249 start=0
250 if self.opts.endVariation!=None:
251 end=self.opts.endVariation
252 if end>=len(variations):
253 end=len(variations)-1
254 else:
255 end=len(variations)-1
256
257 if self.opts.singleVariation!=None:
258 if self.opts.startVariation or self.opts.endVariation:
259 self.error("--single-variation not possible with --end-variation-number or --start-variation-number")
260 if self.opts.singleVariation<0:
261 self.error("--single-variation must be greater or equal to 0")
262 if self.opts.singleVariation>=len(variations):
263 self.error("Only",len(variations))
264 start=self.opts.singleVariation
265 end =self.opts.singleVariation
266
267 if end<start:
268 self.error("Start value",start,"bigger than end value",end)
269
270 if self.opts.listVariations:
271 self.printPhase("Listing variations")
272 for i in range(start,end+1):
273 print_("Variation",i,":",variations[i])
274 return
275
276 if not hasDatabase or self.opts.noDatabaseWrite:
277 if path.exists(self.opts.database) and self.opts.createDatabase:
278 self.error("database-file",self.opts.database,"exists already.")
279 elif not path.exists(self.opts.database) and not self.opts.createDatabase and not self.opts.autoCreateDatabase:
280 self.error("database-file",self.opts.database,"does not exist")
281
282 createDatabase=self.opts.createDatabase
283 if self.opts.autoCreateDatabase and not path.exists(self.opts.database):
284 createDatabase=True
285
286 if not hasDatabase or self.opts.noDatabaseWrite:
287 db=None
288 else:
289 db=RunDatabase(self.opts.database,
290 create=createDatabase,
291 verbose=self.opts.verbose)
292
293 origCase=SolutionDirectory(origPath,archive=None)
294 if self.opts.oneClonedCase:
295 self.printPhase("Cloning work case")
296 workCase=origCase.cloneCase(path.join(self.opts.cloneToDirectory,
297 self.opts.clonedCasePrefix+"_"+path.basename(origPath)))
298
299 self.printPhase("Starting actual variations")
300
301 for i in range(start,end+1):
302 self.printPhase("Variation",i,"of [",start,",",end,"]")
303
304 usedVals=variations[i].copy()
305 usedVals.update(fixed)
306
307 self.prepareHooks()
308
309 clone=False
310 if self.opts.inplaceExecution:
311 workCase=origCase
312 elif self.opts.oneClonedCase:
313 pass
314 else:
315 self.printPhase("Cloning work case")
316 workCase=origCase.cloneCase(path.join(self.opts.cloneToDirectory,
317 self.opts.clonedCasePrefix+"_"+
318 ("%05d" % i)+"_"+path.basename(origPath)))
319
320 self.processPlotLineOptions(autoPath=workCase.name)
321
322 self.printPhase("Setting up the case")
323
324 self.prepare(workCase,overrideParameters=usedVals)
325
326 if self.opts.noExecuteSolver:
327 self.printPhase("Not executing the solver")
328 continue
329
330 if self.opts.oneClonedCase or self.opts.inplaceExecution:
331 self.setLogname(self.opts.clonedCasePrefix+("_%05d_"%i)+usedVals["solver"],
332 useApplication=False,
333 force=True)
334 else:
335 self.setLogname(self.opts.clonedCasePrefix+"_"+usedVals["solver"],
336 useApplication=False,
337 force=True)
338
339 lam=self.getParallel(workCase)
340
341 allLines().clear()
342 allPlots().clear()
343 resetCustomCounter()
344
345 run=AnalyzedRunner(BoundingLogAnalyzer(progress=self.opts.progress,
346 doFiles=self.opts.writeFiles,
347 singleFile=self.opts.singleDataFilesOnly,
348 doTimelines=True),
349 silent=self.opts.progress or self.opts.silent,
350 argv=[usedVals["solver"],"-case",workCase.name],
351 server=self.opts.server,
352 lam=lam,
353 logname=self.opts.logname,
354 compressLog=self.opts.compress,
355 logTail=self.opts.logTail,
356 noLog=self.opts.noLog,
357 remark=self.opts.remark,
358 parameters=usedVals,
359 echoCommandLine=self.opts.echoCommandPrefix,
360 jobId=self.opts.jobId)
361
362 run.createPlots(customRegexp=self.lines_,
363 writeFiles=self.opts.writeFiles)
364
365 self.runPreHooks()
366
367 self.printPhase("Running")
368
369 run.start()
370
371 self.printPhase("Getting data")
372
373 self["run%05d" % i]=run.data
374 if db:
375 db.add(run.data)
376
377 self.runPostHooks()
378
379 self.reportUsage(run)
380 self.reportRunnerData(run)
381
382 self.printPhase("Ending variation")
383
384