1 |
# $Id$ |
2 |
""" A simple tool to handel parameters for a simulation in easy way |
3 |
The idea is that all parameters are stored in a single object in a hierachical form |
4 |
and can accessed using python's attribute notation. For instance: |
5 |
|
6 |
parm.parm2.alpha=814. |
7 |
parm.parm1.gamma=0. |
8 |
parm.parm1.dim=2 |
9 |
parm.parm1.tol_v=0.001 |
10 |
parm.parm1.output_file="/tmp/u.%3.3d.dx" |
11 |
parm.parm1.runFlag=True |
12 |
parm.parm1.T=1. |
13 |
parm.parm1.x1=[-1.,2] |
14 |
parm.parm1.x2=(10.,11) |
15 |
parm.parm1.x3=(-10.,) |
16 |
parm.parm1.parm11.gamma1=1. |
17 |
parm.parm1.parm11.gamma2=2. |
18 |
parm.parm1.parm11.gamma3=3. |
19 |
|
20 |
This structure can be stored/defined through an XML file parm.xml: |
21 |
|
22 |
<?xml version="1.0"?> |
23 |
<ESyS> |
24 |
<Component type="Geodynamics"> |
25 |
<Name>parm1</Name> |
26 |
<Description> |
27 |
a few examples of parameters |
28 |
</Description> |
29 |
<Parameter><Item>gamma</Item><Value>0.</Value></Parameter> |
30 |
<Parameter type="int"><Item>dim</Item><Value>2</Value></Parameter> |
31 |
<Parameter type="real"><Item>tol_v</Item><Value>0.001</Value></Parameter> |
32 |
<Parameter type="string"><Item>output_file</Item><Value>/tmp/u.%3.3d.dx</Value></Parameter> |
33 |
<Parameter type="bool"><Item>runFlag</Item><Value>true</Value></Parameter> |
34 |
<Parameter type="real" sequence="single"><Item>T</Item><Value>1.</Value><Value>2</Value></Parameter> |
35 |
<Parameter type="real" sequence="list"><Item>x1</Item><Value>-1.</Value><Value>2</Value></Parameter> |
36 |
<Parameter type="real" sequence="tuple"><Item>x2</Item><Value>10</Value><Value>11</Value></Parameter> |
37 |
<Parameter sequence="tuple"><Item>x3</Item><Value>-10.</Value></Parameter> |
38 |
<Component> |
39 |
<Name>parm11</Name> |
40 |
<Description> |
41 |
a sub compoment |
42 |
</Description> |
43 |
<Parameter><Item>gamma1</Item><Value>1.</Value></Parameter> |
44 |
<Parameter><Item>gamma2</Item><Value>2.</Value></Parameter> |
45 |
<Parameter><Item>gamma3</Item><Value>3.</Value></Parameter> |
46 |
</Component> |
47 |
</Component> |
48 |
<Component type="Geodynamics"> |
49 |
<Name>parm2</Name> |
50 |
<Description> |
51 |
another component |
52 |
</Description> |
53 |
<Parameter><Item>alpha</Item><Value>0814</Value></Parameter> |
54 |
</Component> |
55 |
</ESyS> |
56 |
|
57 |
""" |
58 |
|
59 |
import types |
60 |
from xml.dom import minidom |
61 |
from string import atoi,atof |
62 |
|
63 |
class ESySParameters: |
64 |
"""is an object to store simulation parameters in the form of a tree and |
65 |
access their values in an easy form |
66 |
|
67 |
Leaves of an ESySParameters objects can be |
68 |
|
69 |
a single real number or a list or tuple of real numbers |
70 |
a single integer number or a list or tuple of integer numbers |
71 |
a single strings or a list or tuple of strings |
72 |
a single boolean value or a list or tuple of boolean values |
73 |
a ESySParameters object |
74 |
any other object (not considered by writeESySXML and writeXML) |
75 |
|
76 |
Example how to create an ESySParameters object: |
77 |
|
78 |
parm=ESySParameters() |
79 |
parm.parm1=ESySParameters() |
80 |
parm.parm1.gamma=0. |
81 |
parm.parm1.dim=2 |
82 |
parm.parm1.tol_v=0.001 |
83 |
parm.parm1.output_file="/tmp/u.%3.3d.dx" |
84 |
parm.parm1.runFlag=True |
85 |
parm.parm1.T=1. |
86 |
parm.parm1.x1=[-1.,2] |
87 |
parm.parm1.x2=(10.,11) |
88 |
parm.parm1.x3=(-10.,) |
89 |
parm.parm1.parm11=ESySParameters() |
90 |
parm.parm1.parm11.gamma1=1. |
91 |
parm.parm1.parm11.gamma2=2. |
92 |
parm.parm1.parm11.gamma3=3. |
93 |
parm.parm2=ESySParameters() |
94 |
parm.parm2.alpha=814. |
95 |
|
96 |
print parm |
97 |
|
98 |
Output is |
99 |
|
100 |
(parm1=(dim=2,output_file=/tmp/u.%3.3d.dx,parm11=(gamma3=3.0,gamma2=2.0,gamma1=1.0), |
101 |
tol_v=0.001,T=1.0,x2=(10,),x3=(-10,),runFlag=True,x1=[-1.0, 2.0],gamma=0.0),parm2=(alpha=814.0)) |
102 |
|
103 |
Notice that parm.parm1.x1 is now a list of two floats although it is defined by a list of a float and an integer. |
104 |
ESySParameter is trying to use the same type for all items in a list or a tuple. |
105 |
|
106 |
""" |
107 |
|
108 |
def __init__(self,description="none",type=None): |
109 |
self.setDescription(description) |
110 |
self.setType(type) |
111 |
|
112 |
def getTypeName(self): |
113 |
if self.__type==None: |
114 |
return None |
115 |
else: |
116 |
return self.__type.__str__() |
117 |
|
118 |
def getDescription(self): |
119 |
return self.__description |
120 |
|
121 |
def setType(self,type=None): |
122 |
self.__type=type |
123 |
|
124 |
def setDescription(self,description="none"): |
125 |
self.__description=description |
126 |
|
127 |
def __str__(self): |
128 |
"""returns a string representation""" |
129 |
out="" |
130 |
for name,value in self.__dict__.iteritems(): |
131 |
if name[0]!="_": |
132 |
if out=="": |
133 |
out=name+"="+str(value) |
134 |
else: |
135 |
out=out+","+name+"="+str(value) |
136 |
return "("+out+")" |
137 |
|
138 |
def __setattr__(self,name,value): |
139 |
"""defines attribute name and assigns value. if name does not start |
140 |
with an underscore value has to be a valid Parameter.""" |
141 |
name=name.replace(" ","_") |
142 |
if name[0]!="_": |
143 |
if value==None: |
144 |
self.__dict__[name]=value |
145 |
elif isinstance(value,ESySParameters): |
146 |
self.__dict__[name]=value |
147 |
elif isinstance(value,types.BooleanType): |
148 |
self.__dict__[name]=value |
149 |
elif isinstance(value,types.ListType): |
150 |
self.__dict__[name]=_mkSameType(value) |
151 |
elif isinstance(value,types.TupleType): |
152 |
self.__dict__[name]=tuple(_mkSameType(value)) |
153 |
elif isinstance(value,types.BooleanType): |
154 |
self.__dict__[name]=value |
155 |
elif isinstance(value,types.IntType): |
156 |
self.__dict__[name]=value |
157 |
elif isinstance(value,types.FloatType): |
158 |
self.__dict__[name]=value |
159 |
elif isinstance(value,types.StringType) or isinstance(value,types.UnicodeType): |
160 |
self.__dict__[name]=str(value) |
161 |
else: |
162 |
self.__dict__[name]=value |
163 |
else: |
164 |
self.__dict__[name]=value |
165 |
|
166 |
def writeXML(self,iostream): |
167 |
"""writes the object as an XML object into an IO stream""" |
168 |
for name,value in self.__dict__.iteritems(): |
169 |
if name[0]!="_": |
170 |
if isinstance(value,ESySParameters): |
171 |
sequence=_PARAMETER_SEQUENCE_UNKNOWN |
172 |
type=value.getTypeName() |
173 |
iostream.write("<%s"%_COMPONENT) |
174 |
if type!=None: iostream.write("%s=\"%s\""%(_COMPONENT_TYPE_ATTRIBUTE,type)) |
175 |
iostream.write(">\n<%s>%s</%s>\n<%s>%s</%s>\n"%(_NAME,name,_NAME,_DESCRIPTION,value.getDescription(),_DESCRIPTION)) |
176 |
value.writeXML(iostream) |
177 |
iostream.write("</%s>"%_COMPONENT) |
178 |
else: |
179 |
if isinstance(value,types.ListType): |
180 |
sequence=_PARAMETER_SEQUENCE_LIST |
181 |
type=_getTypeNameOfList(value) |
182 |
elif isinstance(value,types.TupleType): |
183 |
sequence=_PARAMETER_SEQUENCE_TUPLE |
184 |
type=_getTypeNameOfList(value) |
185 |
else: |
186 |
sequence=_PARAMETER_SEQUENCE_SINGLE |
187 |
type=_getTypeName(value) |
188 |
iostream.write("<%s %s=\"%s\" %s=\"%s\"><%s>%s</%s>\n"% \ |
189 |
(_PARAMETER,_PARAMETER_TYPE_ATTRIBUTE,type, \ |
190 |
_PARAMETER_SEQUENCE_ATTRIBUTE,sequence, \ |
191 |
_PARAMETER_ITEM,name,_PARAMETER_ITEM)) |
192 |
if type!=_PARAMETER_TYPE_UNKNOWN: |
193 |
if sequence==_PARAMETER_SEQUENCE_LIST or sequence==_PARAMETER_SEQUENCE_TUPLE: |
194 |
for i in value: |
195 |
iostream.write("<%s>%s</%s>"%(_PARAMETER_VALUE,i.__str__(),_PARAMETER_VALUE)) |
196 |
elif sequence==_PARAMETER_SEQUENCE_SINGLE: |
197 |
iostream.write("<%s>%s</%s>\n"%(_PARAMETER_VALUE,value.__str__(),_PARAMETER_VALUE)) |
198 |
iostream.write("</%s>\n"%_PARAMETER) |
199 |
|
200 |
|
201 |
def writeESySXML(self,iostream): |
202 |
"""writes an ESyS XML file""" |
203 |
iostream.write("<?xml version=\"1.0\"?><ESyS>") |
204 |
self.writeXML(iostream) |
205 |
iostream.write("</ESyS>") |
206 |
|
207 |
def readESySXMLFile(filename): |
208 |
"""reads an ESyS XML file and returns it as a ESySParameter object""" |
209 |
return _readParametersFromDOM(minidom.parse(filename).getElementsByTagName(_ESYS)[0]) |
210 |
|
211 |
|
212 |
_ESYS="ESyS" |
213 |
_COMPONENT="Component" |
214 |
_COMPONENT_TYPE_ATTRIBUTE="type" |
215 |
_NAME="Name" |
216 |
_DESCRIPTION="Description" |
217 |
_PARAMETER="Parameter" |
218 |
_PARAMETER_ITEM="Item" |
219 |
_PARAMETER_VALUE="Value" |
220 |
_PARAMETER_TYPE_ATTRIBUTE="type" |
221 |
_PARAMETER_TYPE_REAL="real" |
222 |
_PARAMETER_TYPE_INT="int" |
223 |
_PARAMETER_TYPE_STRING="string" |
224 |
_PARAMETER_TYPE_BOOL="bool" |
225 |
_PARAMETER_TYPE_UNKNOWN="unknown" |
226 |
_PARAMETER_SEQUENCE_ATTRIBUTE="sequence" |
227 |
_PARAMETER_SEQUENCE_UNKNOWN="unknown" |
228 |
_PARAMETER_SEQUENCE_SINGLE="single" |
229 |
_PARAMETER_SEQUENCE_LIST="list" |
230 |
_PARAMETER_SEQUENCE_TUPLE="tuple" |
231 |
|
232 |
|
233 |
def _mkSameType(list): |
234 |
"""returns list where all items in the list have the same type""" |
235 |
out=[] |
236 |
if len(list)>0: |
237 |
type=0 |
238 |
for i in list: |
239 |
if isinstance(i,types.BooleanType): |
240 |
type=max(type,0) |
241 |
elif isinstance(i,types.IntType): |
242 |
type=max(type,1) |
243 |
elif isinstance(i,types.FloatType): |
244 |
type=max(type,2) |
245 |
elif isinstance(i,types.StringType): |
246 |
type=max(type,3) |
247 |
else: |
248 |
raise TypeError,"illegal item type" |
249 |
|
250 |
for i in list: |
251 |
if isinstance(i,types.BooleanType): |
252 |
if type==0: |
253 |
out.append(i) |
254 |
elif type==1: |
255 |
out.append(int(i)) |
256 |
elif type==2: |
257 |
out.append(float(i)) |
258 |
else: |
259 |
out.append(i.__str__()) |
260 |
elif isinstance(i,types.IntType): |
261 |
if type==1: |
262 |
out.append(i) |
263 |
elif type==2: |
264 |
out.append(float(i)) |
265 |
else: |
266 |
out.append(i.__str__()) |
267 |
elif isinstance(i,types.FloatType): |
268 |
if type==2: |
269 |
out.append(i) |
270 |
else: |
271 |
out.append(i.__str__()) |
272 |
else: |
273 |
out.append(i) |
274 |
return out |
275 |
|
276 |
def _getTypeNameOfList(values): |
277 |
"""returns the type of the parameters in list values""" |
278 |
if len(values)==0: |
279 |
type=_PARAMETER_TYPE_UNKNOWN |
280 |
else: |
281 |
type=_getTypeName(values[0]) |
282 |
return type |
283 |
|
284 |
def _getTypeName(value): |
285 |
"""returns the type of the parameter value""" |
286 |
if isinstance(value,types.FloatType): |
287 |
type=_PARAMETER_TYPE_REAL |
288 |
elif isinstance(value,types.BooleanType): |
289 |
type=_PARAMETER_TYPE_BOOL |
290 |
elif isinstance(value,types.IntType): |
291 |
type=_PARAMETER_TYPE_INT |
292 |
elif isinstance(value,types.StringType): |
293 |
type=_PARAMETER_TYPE_STRING |
294 |
else: |
295 |
type=_PARAMETER_TYPE_UNKNOWN |
296 |
return type |
297 |
|
298 |
|
299 |
def _extractStrippedValue(dom): |
300 |
"""exracts a string from a DOM node""" |
301 |
out="" |
302 |
for i in dom.childNodes: |
303 |
s=i.nodeValue.strip() |
304 |
if s!="\n": out+=s |
305 |
return str(out) |
306 |
|
307 |
def _readParametersFromDOM(dom): |
308 |
out=ESySParameters() |
309 |
for node in dom.childNodes: |
310 |
if node.nodeType==node.ELEMENT_NODE: |
311 |
if node.nodeName==_COMPONENT: |
312 |
name=None |
313 |
description="none" |
314 |
type=None |
315 |
if node.hasAttribute(_COMPONENT_TYPE_ATTRIBUTE): type=node.getAttribute(_COMPONENT_TYPE_ATTRIBUTE) |
316 |
# find description and name: |
317 |
for c_dom in node.childNodes: |
318 |
if c_dom.nodeType==c_dom.ELEMENT_NODE: |
319 |
if c_dom.tagName==_NAME: name=_extractStrippedValue(c_dom) |
320 |
if c_dom.tagName==_DESCRIPTION: description=_extractStrippedValue(c_dom) |
321 |
if name==None: |
322 |
raise IOError,"name of component missing" |
323 |
p=_readParametersFromDOM(node) |
324 |
p.setDescription(description) |
325 |
p.setType(type) |
326 |
out.__setattr__(name,p) |
327 |
elif node.nodeName==_PARAMETER: |
328 |
if node.hasAttribute(_PARAMETER_TYPE_ATTRIBUTE): |
329 |
type=node.getAttribute(_PARAMETER_TYPE_ATTRIBUTE) |
330 |
if type==_PARAMETER_TYPE_UNKNOWN: type=_PARAMETER_TYPE_REAL |
331 |
else: |
332 |
type=_PARAMETER_TYPE_REAL |
333 |
if node.hasAttribute(_PARAMETER_SEQUENCE_ATTRIBUTE): |
334 |
sequence=node.getAttribute(_PARAMETER_SEQUENCE_ATTRIBUTE) |
335 |
if sequence==_PARAMETER_SEQUENCE_UNKNOWN: sequence=_PARAMETER_SEQUENCE_SINGLE |
336 |
else: |
337 |
sequence=_PARAMETER_SEQUENCE_SINGLE |
338 |
# get the name and values as list: |
339 |
name=None |
340 |
p=[] |
341 |
for c_dom in node.childNodes: |
342 |
if c_dom.nodeType==c_dom.ELEMENT_NODE: |
343 |
if c_dom.nodeName==_PARAMETER_ITEM: name=_extractStrippedValue(c_dom) |
344 |
if c_dom.nodeName==_PARAMETER_VALUE: |
345 |
value=_extractStrippedValue(c_dom) |
346 |
if type==_PARAMETER_TYPE_REAL: |
347 |
p.append(atof(value)) |
348 |
elif type==_PARAMETER_TYPE_INT: |
349 |
p.append(atoi(value)) |
350 |
elif type==_PARAMETER_TYPE_BOOL: |
351 |
if value=="true" or value=="True" or value=="TRUE": |
352 |
p.append(True) |
353 |
elif value=="false" or value=="FALSE" or value=="False": |
354 |
p.append(False) |
355 |
else: |
356 |
raise IOError,"cannot convert %s to bool"%value |
357 |
elif type==_PARAMETER_TYPE_STRING: |
358 |
p.append(value) |
359 |
else: |
360 |
raise IOError,"unknown parameter type %s"%type |
361 |
if name==None: raise IOError,"Item tag missing" |
362 |
if sequence==_PARAMETER_SEQUENCE_SINGLE: |
363 |
if len(p)==0: |
364 |
p=None |
365 |
else: |
366 |
p=p[0] |
367 |
elif sequence==_PARAMETER_SEQUENCE_TUPLE: |
368 |
if len(p)==0: |
369 |
p=tuple() |
370 |
else: |
371 |
p=tuple(p) |
372 |
elif sequence==_PARAMETER_SEQUENCE_LIST: |
373 |
pass |
374 |
else: |
375 |
raise IOError,"unknown sequence attribute %s"%sequence |
376 |
out.__setattr__(name,p) |
377 |
return out |
378 |
|
379 |
# test section: |
380 |
if (__name__=="__main__"): |
381 |
def test(parm): |
382 |
if parm.parm1.gamma!=0. : raise IOError,"unexpected value for parm.parm1.gamma" |
383 |
if parm.parm1.dim!=2: raise IOError,"unexpected value for parm.parm1.dim" |
384 |
if parm.parm1.tol_v!=0.001: raise IOError,"unexpected value for parm.parm1.tol_v" |
385 |
if parm.parm1.output_file!="/tmp/u.%3.3d.dx": raise IOError,"unexpected value for parm.parm1.output_file" |
386 |
if parm.parm1.runFlag!=True: raise IOError,"unexpected value for parm.parm1.runFlag" |
387 |
if parm.parm1.T!=1.: raise IOError,"unexpected value for parm.parm1.T" |
388 |
if parm.parm1.x1[0]!=-1.: raise IOError,"unexpected value for parm.parm1.x1[0]" |
389 |
if parm.parm1.x1[1]!=2.: raise IOError,"unexpected value for parm.parm1.x1[1]" |
390 |
if parm.parm1.x2[0]!=10.: raise IOError,"unexpected value for parm.parm1.x2[0]" |
391 |
if parm.parm1.x2[1]!=11.: raise IOError,"unexpected value for parm.parm1.x2[1]" |
392 |
if parm.parm1.x3[0]!=-10: raise IOError,"unexpected value for parm.parm1.x3[0]" |
393 |
if parm.parm1.parm11.gamma1!=1.: raise IOError,"unexpected value for parm.parm1.parm11.gamma1" |
394 |
if parm.parm1.parm11.gamma2!=2.: raise IOError,"unexpected value for parm.parm1.parm11.gamma2" |
395 |
if parm.parm1.parm11.gamma3!=3.: raise IOError,"unexpected value for parm.parm1.parm11.gamma3" |
396 |
if parm.parm2.alpha!=814.: raise IOError,"unexpected value for parm.parm2.alpha" |
397 |
|
398 |
print "@@@ explicit construction" |
399 |
parm=ESySParameters() |
400 |
parm.parm1=ESySParameters() |
401 |
parm.parm1.gamma=0. |
402 |
parm.parm1.dim=2 |
403 |
parm.parm1.tol_v=0.001 |
404 |
parm.parm1.output_file="/tmp/u.%3.3d.dx" |
405 |
parm.parm1.runFlag=True |
406 |
parm.parm1.T=1. |
407 |
parm.parm1.x1=[-1.,2] |
408 |
parm.parm1.x2=(10,11.) |
409 |
parm.parm1.x3=(-10.,) |
410 |
parm.parm1.parm11=ESySParameters() |
411 |
parm.parm1.parm11.gamma1=1. |
412 |
parm.parm1.parm11.gamma2=2. |
413 |
parm.parm1.parm11.gamma3=3. |
414 |
parm.parm2=ESySParameters() |
415 |
parm.parm2.alpha=814. |
416 |
print parm |
417 |
test(parm) |
418 |
print "@@@ read and write:" |
419 |
parm.writeESySXML(file("/tmp/test.xml",mode="w")) |
420 |
parm2=readESySXMLFile("/tmp/test.xml") |
421 |
print parm2 |
422 |
test(parm2) |
423 |
print "@@@ file" |
424 |
file("/tmp/test2.xml","w").write("""<?xml version="1.0"?> |
425 |
<ESyS> |
426 |
<Component type="Geodynamics"> |
427 |
<Name>parm1</Name> |
428 |
<Description> |
429 |
a few examples of parameters |
430 |
</Description> |
431 |
<Parameter><Item>gamma</Item><Value>0.</Value></Parameter> |
432 |
<Parameter type="int"><Item>dim</Item><Value>2</Value></Parameter> |
433 |
<Parameter type="real"><Item>tol_v</Item><Value>0.001</Value></Parameter> |
434 |
<Parameter type="string"><Item>output_file</Item><Value>/tmp/u.%3.3d.dx</Value></Parameter> |
435 |
<Parameter type="bool"><Item>runFlag</Item><Value>true</Value></Parameter> |
436 |
<Parameter type="real" sequence="single"><Item>T</Item><Value>1.</Value><Value>2</Value></Parameter> |
437 |
<Parameter type="real" sequence="list"><Item>x1</Item><Value>-1.</Value><Value>2</Value></Parameter> |
438 |
<Parameter type="real" sequence="tuple"><Item>x2</Item><Value>10</Value><Value>11</Value></Parameter> |
439 |
<Parameter sequence="tuple"><Item>x3</Item><Value>-10</Value></Parameter> |
440 |
<Component> |
441 |
<Name>parm11</Name> |
442 |
<Description> |
443 |
a sub compoment |
444 |
</Description> |
445 |
<Parameter><Item>gamma1</Item><Value>1.</Value></Parameter> |
446 |
<Parameter><Item>gamma2</Item><Value>2.</Value></Parameter> |
447 |
<Parameter><Item>gamma3</Item><Value>3.</Value></Parameter> |
448 |
</Component> |
449 |
</Component> |
450 |
<Component type="Geodynamics"> |
451 |
<Name>parm2</Name> |
452 |
<Description> |
453 |
another component |
454 |
</Description> |
455 |
<Parameter><Item>alpha</Item><Value>0814</Value></Parameter> |
456 |
</Component> |
457 |
</ESyS> |
458 |
""") |
459 |
parm3=readESySXMLFile("/tmp/test2.xml") |
460 |
print parm3 |
461 |
test(parm3) |