1 """
2 Represents the actual CaseBuilder-File and other things that have to do with the Casebuilder
3 """
4
5 from xml.dom.minidom import parse
6 import xml.dom
7 from os import path
8 import os
9 import shutil
10 import glob
11
12 from PyFoam.Error import error,warning
13 from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile,FoamStringParser
14 from PyFoam.Execution.BasicRunner import BasicRunner
15 from PyFoam.FoamInformation import oldAppConvention as oldApp
16 from .CreateBoundaryPatches import CreateBoundaryPatches
17 from PyFoam import configuration as config
18 from PyFoam.Basics.DataStructures import Vector
19
20 from PyFoam.ThirdParty.six import exec_
21
23 """Gets a list of the case-builder files found in the current path"""
32
34 for f in self.list:
35 yield f
36
39
42
44 """Wraps the argument element for convenient access"""
47
49 if name=="type":
50 tmp=self.el.getElementsByTagName("verify")
51 if len(tmp)!=1:
52 return None
53 val=tmp[0].getElementsByTagName("validator")
54 if len(val)!=1:
55 return None
56 else:
57 return val[0].getAttribute("type")
58 elif name=="values":
59 tmp=self.el.getElementsByTagName("verify")
60 if len(tmp)!=1:
61 return None
62 val=tmp[0].getElementsByTagName("validator")
63 if len(val)!=1:
64 return None
65 else:
66 return list(map(str,val[0].getAttribute("values").split("|")))
67 else:
68 return self.el.getAttribute(name)
69
71 """
72 This class reads an XML-file that describes how to build a case
73 and gives information about the case and if asked to builds the actual case
74 """
75
77 """@param fName: the XML-file that describes how to build the case"""
78
79 dom=parse(fName)
80 self.doc=dom.documentElement
81
82 if self.doc.tagName!='casebuilder':
83 error("Wrong root-element",self.doc.tagName,"Expected: 'casebuilder'")
84
86 return self.doc.getAttribute("name")
87
89 return self.doc.getAttribute("description")
90
92 ht=self.getSingleElement(self.doc,"helptext",optional=True)
93 if ht:
94 return ht.firstChild.nodeValue
95 else:
96 return "<No help text>"
97
100
103
106
109
112
115
117 return self.expandVars(self.doc.getAttribute("template"))
118
120 tmp=self.doc.getAttribute("initialdir")
121 if tmp=="":
122 tmp="0"
123 return tmp
124
126 orig=path.expanduser(orig)
127 orig=path.expandvars(orig)
128 if keys!=None:
129 orig=orig % keys
130 return orig
131
139
147
149 res={}
150 for nm,pat in self.boundaryPatterns():
151 res[nm]=pat
152 return res
153
155 bounds={}
156
157 for a in self.boundaryTree().getElementsByTagName("boundary"):
158 bounds[a.getAttribute("name")]=a.getAttribute("description")
159
160 return bounds
161
163 args=[]
164
165 for a in self.argTree().getElementsByTagName("argumentgroup"):
166 args.append(a.getAttribute("name"))
167
168 return args
169
171 args={}
172
173 for a in self.argTree().getElementsByTagName("argumentgroup"):
174 args[a.getAttribute("name")]=a.getAttribute("description")
175
176 return args
177
179 args=[]
180
181 for a in self.argTree().getElementsByTagName("arg"):
182 args.append(a.getAttribute("name"))
183
184 return args
185
187 args={}
188
189 for a in self.argTree().getElementsByTagName("arg"):
190 args[a.getAttribute("name")]=ArgWrapper(a)
191
192 return args
193
195 """Returns a list with the arguments belongin to a specific group
196 @param name: Name of the group. If none is given, then all the arguments
197 belonging to no group are returned"""
198
199 result=[]
200
201 for c in self.argTree().childNodes:
202 if "tagName" in dir(c):
203 if c.tagName=="arg" and name==None:
204 result.append(c.getAttribute("name"))
205 elif c.tagName=="argumentgroup":
206 if c.getAttribute("name")==name:
207 for e in c.getElementsByTagName("arg"):
208 result.append(e.getAttribute("name"))
209
210 return result
211
213 args={}
214
215 for a in self.argTree().getElementsByTagName("arg"):
216 args[a.getAttribute("name")]=a.getAttribute("description")
217
218 return args
219
221 args={}
222
223 for a in self.argTree().getElementsByTagName("arg"):
224 args[a.getAttribute("name")]=a.getAttribute("default")
225
226 return args
227
229 """Get an element and check that it is the only one
230 @param parent: the parent element
231 @param name: The name of the element"""
232
233 tmp=parent.getElementsByTagName(name)
234 if len(tmp)<1:
235 if optional:
236 return None
237 else:
238 error("Element",name,"does not exist")
239 if len(tmp)>1:
240 error("More than one element",name,"does exist")
241 return tmp[0]
242
244 result="'type':'"+node.getAttribute("type")+"'"
245 para=self.expandVars(node.getAttribute("parameters"),args)
246 if para!="":
247 result+=","+para
248 return "{"+result+"}"
249
251 """Validate the arguments with the provided code (if it exists)"""
252 totalMsg=""
253 for a in self.argTree().getElementsByTagName("arg"):
254 msg=None
255 nm=a.getAttribute("name")
256 verify=self.getSingleElement(a,"verify",optional=True)
257 if verify:
258 valid=self.getSingleElement(verify,"validator",optional=True)
259 script=self.getSingleElement(verify,"script",optional=True)
260 if valid and script:
261 error("Variable",nm,"has script and validator. Only one of them supported")
262 elif valid:
263 typ=valid.getAttribute("type")
264 arg=args[nm]
265 isNumeric=False
266 if typ=="float":
267 isNumeric=True
268 try:
269 tmp=float(arg)
270 except ValueError:
271 msg="Not a floating point number"
272 elif typ=="integer":
273 isNumeric=True
274 try:
275 tmp=int(arg)
276 except ValueError:
277 msg="Not an integer number"
278 elif typ=="file":
279 if not path.exists(arg):
280 msg="File "+arg+" does not exist"
281 elif typ=="selection":
282 vals=valid.getAttribute("values").split("|")
283 if not arg in vals:
284 msg="Not in list of possible values: "+", ".join(vals)
285 elif typ=="vector":
286 tmp=FoamStringParser("a "+arg+";")
287 if type(tmp['a'])!=Vector:
288 msg="Is not a valid vector"
289 else:
290 error("No validator implemented for type",typ,"in variable",nm)
291 if isNumeric and not msg:
292 if valid.hasAttribute("min"):
293 if float(arg)<float(valid.getAttribute("min")):
294 msg="Must be bigger than "+valid.getAttribute("min")
295 if valid.hasAttribute("max"):
296 if float(arg)>float(valid.getAttribute("max")):
297 msg="Must be smaller than "+valid.getAttribute("max")
298
299 elif script:
300 if script.getAttribute("plugin")!="python":
301 error("Only plugin-type 'python' is supported for variable",nm)
302 code=script.firstChild.nodeValue
303 arg=args[nm]
304 exec_(code)
305 if msg:
306 totalMsg+=nm+": "+msg+" "
307
308 if totalMsg=="":
309 totalMsg=None
310
311 return totalMsg
312
314 """Add derived variables to the argument dictionary"""
315
316 for _a_ in _args_:
317 exec_("%s = '%s'" % (_a_,_args_[_a_]))
318
319 if self.varTree():
320 for _a_ in self.varTree().getElementsByTagName("var"):
321 _nm_=_a_.getAttribute("name")
322 if _nm_ in ["_args_","_a_","_nm_"]:
323 error("Variable",_nm_,"is needed for this routine to work")
324
325 if len(_a_.firstChild.nodeValue)>0:
326 exec_(_a_.firstChild.nodeValue)
327 exec_("_args_['"+_nm_+"']=str("+_nm_+")")
328
329 return _args_
330
332 """Builds the case
333 @param cName: The name of the case directory
334 @param args: The arguments (as a dictionary)"""
335
336 args=self.calculateVariables(args)
337
338 os.mkdir(cName)
339
340 for d in self.parameterTree().getElementsByTagName("directory"):
341 dName=path.join(cName,d.getAttribute("name"))
342 if not path.isdir(dName):
343 os.mkdir(dName)
344 sName=path.join(self.templatePath(),d.getAttribute("name"))
345 for f in d.getElementsByTagName("file"):
346 dFile=path.join(dName,f.getAttribute("name"))
347 shutil.copy(path.join(sName,f.getAttribute("name")),dFile)
348 if len(f.getElementsByTagName("parameter"))>0:
349 pf=ParsedParameterFile(dFile)
350 for p in f.getElementsByTagName("parameter"):
351 pName=p.getAttribute("name")
352 pValue=self.expandVars(p.getAttribute("value"),args)
353 exec_("pf"+pName+"="+pValue)
354 pf.writeFile()
355
356 prep=self.getSingleElement(self.doc,"meshpreparation")
357 util=prep.getElementsByTagName("utility")
358 copy=self.getSingleElement(prep,"copy",optional=True)
359
360 if len(util)>0 and copy:
361 error("Copy and utilitiy mesh preparation specified")
362 elif len(util)>0:
363 for u in util:
364 app=u.getAttribute("command")
365 arg=self.expandVars(u.getAttribute("arguments"),args)
366 argv=[app,"-case",cName]+arg.split()
367 if oldApp():
368 argv[1]="."
369 run=BasicRunner(argv=argv,silent=True,logname="CaseBuilder.prepareMesh."+app)
370 run.start()
371 if not run.runOK():
372 error(app,"failed. Check the logs")
373 elif copy:
374 source=self.expandVars(copy.getAttribute("template"),args)
375 time=self.expandVars(copy.getAttribute("time"),args)
376 if time=="":
377 time="constant"
378 shutil.copytree(path.join(source,time,"polyMesh"),
379 path.join(cName,"constant","polyMesh"))
380 else:
381 error("Neither copy nor utilitiy mesh preparation specified")
382
383 dName=path.join(cName,self.initialDir())
384 if not path.isdir(dName):
385 os.mkdir(dName)
386 sName=path.join(self.templatePath(),self.initialDir())
387 for f in self.fieldTree().getElementsByTagName("field"):
388 dFile=path.join(dName,f.getAttribute("name"))
389 shutil.copy(path.join(sName,f.getAttribute("name")),dFile)
390 default=self.makeBC(self.getSingleElement(f,"defaultbc"),args)
391
392 CreateBoundaryPatches(args=["--fix-types",
393 "--overwrite",
394 "--clear",
395 "--default="+default,
396 dFile])
397 bcDict={}
398 bounds=self.boundaries()
399 for b in f.getElementsByTagName("bc"):
400 nm=b.getAttribute("name")
401 if nm not in bounds:
402 error("Boundary",nm,"not in list",bounds,"for field",f.getAttribute("name"))
403 bcDict[nm]=b
404
405 for name,pattern in self.boundaryPatterns():
406 if name in bcDict:
407 default=self.makeBC(bcDict[name],args)
408 CreateBoundaryPatches(args=["--filter="+pattern,
409 "--overwrite",
410 "--default="+default,
411 dFile])
412
413 ic=self.expandVars(self.getSingleElement(f,"ic").getAttribute("value"),args)
414 pf=ParsedParameterFile(dFile)
415 pf["internalField"]="uniform "+ic
416 pf.writeFile()
417
418
419