/[escript]/trunk/escript/py_src/modelframe.py
ViewVC logotype

Annotation of /trunk/escript/py_src/modelframe.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 926 - (hide annotations)
Fri Jan 12 06:31:37 2007 UTC (12 years, 9 months ago) by gross
File MIME type: text/x-python
File size: 38095 byte(s)
modelframe support dictonaries with fixed type of key and value. only writing implemented yet
1 jgs 121 # $Id$
2    
3 gross 637 """
4     Environment for implementing models in escript
5    
6     @var __author__: name of author
7     @var __copyright__: copyrights
8     @var __license__: licence agreement
9     @var __url__: url entry point on documentation
10     @var __version__: version
11     @var __date__: date of the version
12     """
13    
14     __author__="Lutz Gross, l.gross@uq.edu.au"
15 elspeth 609 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
16     http://www.access.edu.au
17     Primary Business: Queensland, Australia"""
18 elspeth 614 __license__="""Licensed under the Open Software License version 3.0
19     http://www.opensource.org/licenses/osl-3.0.php"""
20 gross 637 __url__="http://www.iservo.edu.au/esys"
21     __version__="$Revision$"
22     __date__="$Date$"
23 elspeth 609
24 gross 637
25 jgs 122 from types import StringType,IntType,FloatType,BooleanType,ListType,DictType
26     from sys import stdout
27 elspeth 871 import numarray
28     import operator
29 jgs 123 import itertools
30 jgs 121
31 jgs 123 # import the 'set' module if it's not defined (python2.3/2.4 difference)
32     try:
33     set
34     except NameError:
35     from sets import Set as set
36    
37 jgs 122 from xml.dom import minidom
38 jgs 121
39    
40 jgs 123 def all(seq):
41     for x in seq:
42     if not x:
43     return False
44     return True
45    
46 jgs 147 def any(seq):
47     for x in seq:
48     if x:
49     return True
50     return False
51    
52 jgs 150 def importName(modulename, name):
53     """ Import a named object from a module in the context of this function,
54     which means you should use fully qualified module paths.
55     Return None on failure.
56    
57     This function from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52241
58     """
59     module = __import__(modulename, globals(), locals(), [name])
60    
61     try:
62     return vars(module)[name]
63     except KeyError:
64     raise ImportError("Could not import %s from %s" % (name, modulename))
65    
66 gross 918 class ESySXMLParser(object):
67 jgs 147 """
68 gross 918 parser for ESysXML file
69     """
70     def __init__(self,xml, debug=False):
71     self.__dom = minidom.parseString(xml)
72     self.__linkable_object_registry= {}
73     self.__link_registry= []
74     self.__esys=self.__dom.firstChild
75     self.debug=debug
76    
77     def getClassPath(self, node):
78     type = node.getAttribute("type")
79     if (node.getAttribute("module")):
80     module = node.getAttribute("module")
81     return importName(module, type)
82     else:
83     return importName("__main__", type)
84 jgs 123
85 gross 918 def setLinks(self):
86     for obj_id, link in self.__link_registry:
87     link.target = self.__linkable_object_registry[obj_id]
88 jgs 123
89 gross 918 def parse(self):
90     """
91     parser method for EsysXML and returns the list of generating ParameterSets
92     """
93     found=[]
94     for node in self.__esys.childNodes:
95     if isinstance(node, minidom.Element):
96     if node.tagName == 'Simulation':
97     found.append(Simulation.fromDom(self, node))
98     elif node.tagName == 'Model':
99     found.append(self.getClassPath(node).fromDom(self, node))
100     elif node.tagName == 'ParameterSet':
101     found.append(self.getClassPath(node).fromDom(self, node))
102     else:
103     raise "Invalid type, %r" % node.getAttribute("type")
104     self.setLinks()
105     return found
106    
107     def registerLink(self,obj_id, link):
108     self.__link_registry.append((int(obj_id),link))
109    
110     def registerLinkableObject(self,obj, node):
111     id_str=node.getAttribute('id').strip()
112     if len(id_str)>0:
113     id=int(id_str)
114     if self.__linkable_object_registry.has_key(id):
115     raise ValueError("Object id %s already exists."%id)
116     else:
117     self.__linkable_object_registry[id]=obj
118    
119     def getComponent(self, node):
120     """
121     returns a single component + rank from a simulation
122     parser method for EsysXML and returns the list of generating ParameterSets
123     """
124     rank = int(node.getAttribute("rank"))
125     for n in node.childNodes:
126     if isinstance(n, minidom.Element):
127     if n.tagName == 'Simulation':
128     return (rank, Simulation.fromDom(self, n))
129     elif n.tagName == 'Model':
130     return (rank, self.getClassPath(n).fromDom(self, n))
131     elif n.tagName == 'ParameterSet':
132     return (rank, self.getClassPath(n).fromDom(self, n))
133     else:
134     raise ValueError("illegal component type %s"%n.tagName)
135     raise ValueError("cannot resolve Component")
136    
137     class ESySXMLCreator(object):
138     """
139     creates an XML Dom representation
140     """
141     def __init__(self):
142     self.__dom=minidom.Document()
143     self.__esys =self.__dom.createElement('ESys')
144     self.__dom.appendChild(self.__esys)
145     self.__linkable_object_registry={}
146     self.__number_sequence = itertools.count(100)
147     def getRoot(self):
148     return self.__esys
149     def createElement(self,name):
150     return self.__dom.createElement(name)
151     def createTextNode(self,name):
152     return self.__dom.createTextNode(name)
153     def getElementById(self,name):
154     return self.__dom.getElementById(name)
155     def createDataNode(self, tagName, data):
156     """
157     C{createDataNode}s are the building blocks of the xml documents constructed in
158     this module.
159    
160     @param tagName: the associated xml tag
161     @param data: the values in the tag
162     """
163     n = self.createElement(tagName)
164     n.appendChild(self.createTextNode(str(data)))
165     return n
166     def getLinkableObjectId(self, obj):
167     for id, o in self.__linkable_object_registry.items():
168     if o == obj: return id
169     id =self.__number_sequence.next()
170     self.__linkable_object_registry[id]=obj
171     return id
172    
173     def registerLinkableObject(self, obj, node):
174     """
175     returns a unique object id for object obj
176     """
177     id=self.getLinkableObjectId(obj)
178     node.setAttribute('id',str(id))
179     node.setIdAttribute("id")
180    
181     def includeTargets(self):
182     target_written=True
183     while target_written:
184     targetsList =self.__dom.getElementsByTagName('Target')
185     target_written=False
186     for element in targetsList:
187     targetId = int(element.firstChild.nodeValue.strip())
188     if self.getElementById(str(targetId)): continue
189     targetObj = self.__linkable_object_registry[targetId]
190     targetObj.toDom(self, self.__esys)
191     target_written=True
192    
193     def toprettyxml(self):
194     self.includeTargets()
195     return self.__dom.toprettyxml()
196    
197 jgs 122 class Link:
198 jgs 123 """
199 jgs 149 A Link makes an attribute of an object callable::
200    
201 jgs 123 o.object()
202     o.a=8
203     l=Link(o,"a")
204     assert l()==8
205     """
206    
207     def __init__(self,target,attribute=None):
208 jgs 126 """
209 jgs 149 Creates a link to the object target. If attribute is given, the link is
210 jgs 123 establised to this attribute of the target. Otherwise the attribute is
211 jgs 126 undefined.
212     """
213 jgs 123 self.target = target
214     self.attribute = None
215     self.setAttributeName(attribute)
216 gross 912
217     def getAttributeName(self):
218     """
219     returns the name of the attribute the link is pointing to
220     """
221     return self.attribute
222 jgs 123
223     def setAttributeName(self,attribute):
224 jgs 126 """
225 jgs 149 Set a new attribute name to be collected from the target object. The
226 jgs 126 target object must have the attribute with name attribute.
227     """
228 jgs 147 if attribute and self.target:
229     if isinstance(self.target,LinkableObject):
230     if not self.target.hasAttribute(attribute):
231     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
232     else:
233     if not hasattr(self.target,attribute):
234     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
235 jgs 123 self.attribute = attribute
236    
237     def hasDefinedAttributeName(self):
238 jgs 126 """
239 jgs 149 Returns true if an attribute name is set.
240 jgs 126 """
241 jgs 123 return self.attribute != None
242    
243     def __repr__(self):
244 jgs 126 """
245 jgs 149 Returns a string representation of the link.
246 jgs 126 """
247 jgs 123 if self.hasDefinedAttributeName():
248     return "<Link to attribute %s of %s>" % (self.attribute, self.target)
249     else:
250     return "<Link to target %s>" % self.target
251    
252     def __call__(self,name=None):
253 jgs 126 """
254 jgs 149 Returns the value of the attribute of the target object. If the
255 jgs 126 atrribute is callable then the return value of the call is returned.
256     """
257 jgs 123 if name:
258     out=getattr(self.target, name)
259     else:
260     out=getattr(self.target, self.attribute)
261 jgs 121
262 jgs 123 if callable(out):
263     return out()
264     else:
265     return out
266 jgs 121
267 gross 918 def toDom(self, esysxml, node):
268 jgs 126 """
269 jgs 149 C{toDom} method of Link. Creates a Link node and appends it to the
270 gross 918 current XML esysxml.
271 jgs 126 """
272 gross 918 link = esysxml.createElement('Link')
273 jgs 147 assert (self.target != None), ("Target was none, name was %r" % self.attribute)
274 gross 918 link.appendChild(esysxml.createDataNode('Target', esysxml.getLinkableObjectId(self.target)))
275 jgs 123 # this use of id will not work for purposes of being able to retrieve the intended
276     # target from the xml later. I need a better unique identifier.
277     assert self.attribute, "You can't xmlify a Link without a target attribute"
278 gross 918 link.appendChild(esysxml.createDataNode('Attribute', self.attribute))
279 jgs 123 node.appendChild(link)
280 jgs 121
281 gross 918 def fromDom(cls, esysxml, node):
282     targetid = int(node.getElementsByTagName("Target")[0].firstChild.nodeValue.strip())
283     attribute =str(node.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip())
284 jgs 123 l = cls(None, attribute)
285 gross 918 esysxml.registerLink(targetid, l)
286 jgs 123 return l
287 jgs 121
288 jgs 123 fromDom = classmethod(fromDom)
289    
290 jgs 122 class LinkableObject(object):
291 jgs 123 """
292     An object that allows to link its attributes to attributes of other objects
293 jgs 149 via a Link object. For instance::
294 jgs 123
295     p = LinkableObject()
296     p.x = Link(o,"name")
297     print p.x
298    
299 jgs 149 links attribute C{x} of C{p} to the attribute name of object C{o}.
300 jgs 121
301 jgs 149 C{p.x} will contain the current value of attribute C{name} of object
302     C{o}.
303 jgs 121
304 jgs 149 If the value of C{getattr(o, "name")} is callable, C{p.x} will return
305     the return value of the call.
306 jgs 123 """
307    
308    
309 gross 918 def __init__(self, id = None, debug=False):
310 jgs 149 """
311     Initializes LinkableObject so that we can operate on Links
312     """
313 jgs 123 self.debug = debug
314     self.__linked_attributes={}
315 gross 918
316 jgs 123 def trace(self, msg):
317     """
318 jgs 149 If debugging is on, print the message, otherwise do nothing
319     """
320 jgs 123 if self.debug:
321 jgs 147 print "%s: %s"%(str(self),msg)
322 jgs 123
323     def __getattr__(self,name):
324 jgs 149 """
325     Returns the value of attribute name. If the value is a Link object the
326     object is called and the return value is returned.
327     """
328 jgs 123 out = self.getAttributeObject(name)
329     if isinstance(out,Link):
330     return out()
331     else:
332     return out
333    
334     def getAttributeObject(self,name):
335 jgs 149 """
336     Return the object stored for attribute name.
337     """
338 jgs 123
339     if self.__dict__.has_key(name):
340     return self.__dict__[name]
341    
342     if self.__linked_attributes.has_key(name):
343     return self.__linked_attributes[name]
344    
345 jgs 147 if self.__class__.__dict__.has_key(name):
346     return self.__class.__dict__[name]
347    
348 jgs 123 raise AttributeError,"No attribute %s."%name
349    
350 jgs 147 def hasAttribute(self,name):
351 jgs 149 """
352     Returns True if self as attribute name.
353     """
354 jgs 147 return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name)
355    
356 jgs 123 def __setattr__(self,name,value):
357 jgs 149 """
358     Sets the value for attribute name. If value is a Link the target
359     attribute is set to name if no attribute has been specified.
360     """
361 jgs 123
362     if self.__dict__.has_key(name):
363     del self.__dict__[name]
364    
365     if isinstance(value,Link):
366     if not value.hasDefinedAttributeName():
367     value.setAttributeName(name)
368     self.__linked_attributes[name] = value
369    
370 jgs 147 self.trace("attribute %s is now linked by %s."%(name,value))
371 jgs 123 else:
372     self.__dict__[name] = value
373    
374     def __delattr__(self,name):
375 jgs 149 """
376     Removes the attribute name.
377     """
378 jgs 123
379     if self.__linked_attributes.has_key[name]:
380     del self.__linked_attributes[name]
381     elif self.__dict__.has_key(name):
382     del self.__dict__[name]
383     else:
384     raise AttributeError,"No attribute %s."%name
385    
386 jgs 122 class _ParameterIterator:
387 jgs 123 def __init__(self,parameterset):
388 jgs 121
389 jgs 123 self.__set=parameterset
390     self.__iter=iter(parameterset.parameters)
391    
392     def next(self):
393     o=self.__iter.next()
394     return (o,self.__set.getAttributeObject(o))
395    
396     def __iter__(self):
397     return self
398    
399 jgs 122 class ParameterSet(LinkableObject):
400 jgs 149 """
401     A class which allows to emphazise attributes to be written and read to XML
402 jgs 123
403 jgs 149 Leaves of an ESySParameters object can be:
404 jgs 123
405 jgs 149 - a real number
406     - a integer number
407     - a string
408     - a boolean value
409     - a ParameterSet object
410     - a Simulation object
411     - a Model object
412 elspeth 874 - a numarray object
413     - a list of booleans
414     - any other object (not considered by writeESySXML and writeXML)
415 jgs 123
416 jgs 149 Example how to create an ESySParameters object::
417 jgs 123
418 jgs 149 p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.)
419     p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11)
420     parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1")))
421 jgs 123
422 jgs 149 This can be accessed as::
423 jgs 123
424 jgs 149 parm.parm1.gamma=0.
425     parm.parm1.dim=2
426     parm.parm1.tol_v=0.001
427     parm.parm1.output_file="/tmp/u.%3.3d.dx"
428     parm.parm1.runFlag=True
429     parm.parm1.parm11.gamma1=1.
430     parm.parm1.parm11.gamma2=2.
431     parm.parm1.parm11.gamma3=3.
432     parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1)
433 jgs 123 """
434     def __init__(self, parameters=[], **kwargs):
435 jgs 149 """
436     Creates a ParameterSet with parameters parameters.
437     """
438 jgs 123 LinkableObject.__init__(self, **kwargs)
439     self.parameters = set()
440     self.declareParameters(parameters)
441 jgs 147
442     def __repr__(self):
443 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
444 jgs 123
445     def declareParameter(self,**parameters):
446 jgs 149 """
447     Declares a new parameter(s) and its (their) initial value.
448     """
449 jgs 123 self.declareParameters(parameters)
450    
451     def declareParameters(self,parameters):
452 jgs 149 """
453     Declares a set of parameters. parameters can be a list, a dictionary
454     or a ParameterSet.
455     """
456 jgs 123 if isinstance(parameters,ListType):
457     parameters = zip(parameters, itertools.repeat(None))
458     if isinstance(parameters,DictType):
459     parameters = parameters.iteritems()
460 jgs 121
461 jgs 123 for prm, value in parameters:
462     setattr(self,prm,value)
463     self.parameters.add(prm)
464 jgs 121
465 jgs 147 self.trace("parameter %s has been declared."%prm)
466 jgs 121
467 jgs 123 def releaseParameters(self,name):
468 jgs 149 """
469     Removes parameter name from the paramameters.
470     """
471 jgs 123 if self.isParameter(name):
472     self.parameters.remove(name)
473 jgs 147 self.trace("parameter %s has been removed."%name)
474 jgs 123
475     def __iter__(self):
476 jgs 149 """
477     Creates an iterator over the parameter and their values.
478     """
479 jgs 123 return _ParameterIterator(self)
480    
481     def showParameters(self):
482 jgs 149 """
483     Returns a descrition of the parameters.
484     """
485 jgs 123 out="{"
486     notfirst=False
487     for i,v in self:
488     if notfirst: out=out+","
489     notfirst=True
490     if isinstance(v,ParameterSet):
491     out="%s\"%s\" : %s"%(out,i,v.showParameters())
492     else:
493     out="%s\"%s\" : %s"%(out,i,v)
494     return out+"}"
495    
496     def __delattr__(self,name):
497 jgs 149 """
498     Removes the attribute name.
499     """
500 jgs 123 LinkableObject.__delattr__(self,name)
501     try:
502     self.releaseParameter(name)
503     except:
504     pass
505 jgs 121
506 gross 918 def toDom(self, esysxml, node):
507 jgs 149 """
508 gross 918 C{toDom} method of Model class
509 jgs 149 """
510 gross 918 pset = esysxml.createElement('ParameterSet')
511     pset.setAttribute('type', self.__class__.__name__)
512     pset.setAttribute('module', self.__class__.__module__)
513     esysxml.registerLinkableObject(self, pset)
514     self._parametersToDom(esysxml, pset)
515 jgs 123 node.appendChild(pset)
516 jgs 121
517 gross 918 def _parametersToDom(self, esysxml, node):
518 jgs 123 for name,value in self:
519 gross 918 # convert list to numarray when possible:
520     if isinstance (value, list):
521     elem_type=-1
522     for i in value:
523     if isinstance(i, bool):
524     elem_type = max(elem_type,0)
525     if isinstance(i, int) and not isinstance(i, bool):
526     elem_type = max(elem_type,1)
527     if isinstance(i, float):
528     elem_type = max(elem_type,2)
529     if elem_type == 0: value = numarray.array(value,numarray.Bool)
530     if elem_type == 1: value = numarray.array(value,numarray.Int)
531     if elem_type == 2: value = numarray.array(value,numarray.Float)
532    
533     param = esysxml.createElement('Parameter')
534 jgs 123 param.setAttribute('type', value.__class__.__name__)
535 jgs 121
536 gross 918 param.appendChild(esysxml.createDataNode('Name', name))
537 jgs 121
538 gross 918 val = esysxml.createElement('Value')
539 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
540 gross 918 value.toDom(esysxml, val)
541 jgs 123 param.appendChild(val)
542 elspeth 871 elif isinstance(value, numarray.NumArray):
543     shape = value.getshape()
544     if isinstance(shape, tuple):
545     size = reduce(operator.mul, shape)
546     shape = ' '.join(map(str, shape))
547     else:
548     size = shape
549     shape = str(shape)
550    
551     arraytype = value.type()
552 gross 918 if isinstance(arraytype, numarray.BooleanType):
553     arraytype_str="Bool"
554     elif isinstance(arraytype, numarray.IntegralType):
555     arraytype_str="Int"
556     elif isinstance(arraytype, numarray.FloatingType):
557     arraytype_str="Float"
558     elif isinstance(arraytype, numarray.ComplexType):
559     arraytype_str="Complex"
560     else:
561     arraytype_str=str(arraytype)
562     numarrayElement = esysxml.createElement('NumArray')
563     numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
564     numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
565     numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
566 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
567 elspeth 874 val.appendChild(numarrayElement)
568 elspeth 871 param.appendChild(val)
569 gross 918 elif isinstance(value, list):
570     param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
571     elif isinstance(value, (str, bool, int, float, type(None))):
572     param.appendChild(esysxml.createDataNode('Value', str(value)))
573 gross 926 elif isinstance(value, dict):
574     dic = esysxml.createElement('dictionary')
575     if len(value.keys())>0:
576     dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
577     dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
578     for k,v in value.items():
579     i=esysxml.createElement('item')
580     i.appendChild(esysxml.createDataNode('key', k))
581     i.appendChild(esysxml.createDataNode('value', v))
582     dic.appendChild(i)
583     param.appendChild(dic)
584 jgs 123 else:
585 gross 926 print value
586 gross 918 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
587 jgs 123
588     node.appendChild(param)
589    
590 gross 918 def fromDom(cls, esysxml, node):
591 jgs 123 # Define a host of helper functions to assist us.
592     def _children(node):
593     """
594 jgs 149 Remove the empty nodes from the children of this node.
595 jgs 123 """
596 elspeth 871 ret = []
597     for x in node.childNodes:
598     if isinstance(x, minidom.Text):
599     if x.nodeValue.strip():
600     ret.append(x)
601     else:
602     ret.append(x)
603     return ret
604 jgs 123
605 gross 918 def _floatfromValue(esysxml, node):
606     return float(node.nodeValue.strip())
607 jgs 123
608 gross 918 def _stringfromValue(esysxml, node):
609     return str(node.nodeValue.strip())
610 jgs 126
611 gross 918 def _intfromValue(esysxml, node):
612     return int(node.nodeValue.strip())
613 jgs 126
614 gross 918 def _boolfromValue(esysxml, node):
615     return _boolfromstring(node.nodeValue.strip())
616 elspeth 266
617 gross 918 def _nonefromValue(esysxml, node):
618 elspeth 266 return None
619 elspeth 871
620 gross 918 def _numarrayfromValue(esysxml, node):
621     for node in _children(node):
622 elspeth 871 if node.tagName == 'ArrayType':
623     arraytype = node.firstChild.nodeValue.strip()
624     if node.tagName == 'Shape':
625     shape = node.firstChild.nodeValue.strip()
626     shape = [int(x) for x in shape.split()]
627     if node.tagName == 'Data':
628     data = node.firstChild.nodeValue.strip()
629     data = [float(x) for x in data.split()]
630     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
631     shape)
632 elspeth 874
633 gross 918 def _listfromValue(esysxml, node):
634     return [x for x in node.nodeValue.split()]
635 elspeth 874
636     def _boolfromstring(s):
637     if s == 'True':
638     return True
639     else:
640     return False
641 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
642     ptypemap = {"Simulation": Simulation.fromDom,
643     "Model":Model.fromDom,
644     "ParameterSet":ParameterSet.fromDom,
645     "Link":Link.fromDom,
646 elspeth 875 "DataSource":DataSource.fromDom,
647 jgs 123 "float":_floatfromValue,
648 jgs 126 "int":_intfromValue,
649 jgs 123 "str":_stringfromValue,
650 elspeth 269 "bool":_boolfromValue,
651 elspeth 874 "list":_listfromValue,
652     "NumArray":_numarrayfromValue,
653 elspeth 871 "NoneType":_nonefromValue,
654 jgs 123 }
655    
656     parameters = {}
657 gross 918 for n in _children(node):
658     ptype = n.getAttribute("type")
659     if not ptypemap.has_key(ptype):
660     raise KeyError("cannot handle parameter type %s."%ptype)
661 jgs 123
662     pname = pvalue = None
663 gross 918 for childnode in _children(n):
664 jgs 123 if childnode.tagName == "Name":
665     pname = childnode.firstChild.nodeValue.strip()
666    
667     if childnode.tagName == "Value":
668     nodes = _children(childnode)
669 gross 918 pvalue = ptypemap[ptype](esysxml, nodes[0])
670 jgs 123
671     parameters[pname] = pvalue
672    
673     # Create the instance of ParameterSet
674 gross 918 o = cls(debug=esysxml.debug)
675 jgs 123 o.declareParameters(parameters)
676 gross 918 esysxml.registerLinkableObject(o, node)
677 jgs 123 return o
678    
679     fromDom = classmethod(fromDom)
680 gross 918
681 jgs 123 def writeXML(self,ostream=stdout):
682 jgs 149 """
683     Writes the object as an XML object into an output stream.
684     """
685 gross 918 esysxml=ESySXMLCreator()
686     self.toDom(esysxml, esysxml.getRoot())
687     ostream.write(esysxml.toprettyxml())
688    
689 jgs 147 class Model(ParameterSet):
690     """
691 jgs 149 A Model object represents a processess marching over time until a
692     finalizing condition is fullfilled. At each time step an iterative
693     process can be performed and the time step size can be controlled. A
694     Model has the following work flow::
695 gross 906
696 jgs 147 doInitialization()
697 gross 906 while not terminateInitialIteration(): doInitializationiStep()
698     doInitialPostprocessing()
699 jgs 147 while not finalize():
700     dt=getSafeTimeStepSize(dt)
701     doStepPreprocessing(dt)
702     while not terminateIteration(): doStep(dt)
703     doStepPostprocessing(dt)
704     doFinalization()
705 jgs 121
706 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
707     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
708     C{doFinalization} are methods of the particular instance of a Model. The
709     default implementations of these methods have to be overwritten by the
710     subclass implementing a Model.
711 jgs 147 """
712 jgs 121
713 jgs 147 UNDEF_DT=1.e300
714 jgs 121
715 gross 918 def __init__(self,parameters=[],**kwargs):
716 jgs 149 """
717     Creates a model.
718 jgs 121
719 jgs 149 Just calls the parent constructor.
720 jgs 147 """
721 gross 918 ParameterSet.__init__(self, parameters=parameters,**kwargs)
722 jgs 121
723 jgs 147 def __str__(self):
724 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
725 jgs 121
726    
727 jgs 147 def doInitialization(self):
728 jgs 149 """
729     Initializes the time stepping scheme.
730    
731     This function may be overwritten.
732     """
733 jgs 147 pass
734 gross 906 def doInitialStep(self):
735     """
736     performs an iteration step in the initialization phase
737    
738     This function may be overwritten.
739     """
740     pass
741    
742     def terminateInitialIteration(self):
743     """
744     Returns True if iteration at the inital phase is terminated.
745     """
746     return True
747    
748     def doInitialPostprocessing(self):
749     """
750     finalises the initialization iteration process
751    
752     This function may be overwritten.
753     """
754     pass
755 jgs 147
756     def getSafeTimeStepSize(self,dt):
757 jgs 149 """
758     Returns a time step size which can safely be used.
759    
760     C{dt} gives the previously used step size.
761    
762     This function may be overwritten.
763     """
764 jgs 147 return self.UNDEF_DT
765    
766     def finalize(self):
767 jgs 149 """
768     Returns False if the time stepping is finalized.
769    
770     This function may be overwritten.
771     """
772 jgs 147 return False
773 jgs 123
774 jgs 147 def doFinalization(self):
775 jgs 149 """
776     Finalizes the time stepping.
777    
778     This function may be overwritten.
779     """
780 jgs 147 pass
781    
782     def doStepPreprocessing(self,dt):
783 jgs 149 """
784     Sets up a time step of step size dt.
785    
786     This function may be overwritten.
787     """
788 jgs 147 pass
789    
790     def doStep(self,dt):
791 jgs 149 """
792     Executes an iteration step at a time step.
793    
794     C{dt} is the currently used time step size.
795    
796     This function may be overwritten.
797     """
798 jgs 147 pass
799    
800     def terminateIteration(self):
801 jgs 149 """
802     Returns True if iteration on a time step is terminated.
803     """
804 jgs 147 return True
805 gross 906
806 jgs 147
807     def doStepPostprocessing(self,dt):
808 jgs 149 """
809 gross 906 finalises the time step.
810 jgs 149
811     dt is the currently used time step size.
812    
813     This function may be overwritten.
814     """
815 jgs 147 pass
816 gross 918
817     def toDom(self, esysxml, node):
818     """
819     C{toDom} method of Model class
820     """
821     pset = esysxml.createElement('Model')
822     pset.setAttribute('type', self.__class__.__name__)
823     pset.setAttribute('module', self.__class__.__module__)
824     esysxml.registerLinkableObject(self, pset)
825     node.appendChild(pset)
826     self._parametersToDom(esysxml, pset)
827 jgs 147
828     class Simulation(Model):
829     """
830 jgs 149 A Simulation object is special Model which runs a sequence of Models.
831 jgs 121
832 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
833     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
834     C{doFinalization} are executing the corresponding methods of the models in
835     the simulation.
836 jgs 147 """
837    
838     FAILED_TIME_STEPS_MAX=20
839     MAX_ITER_STEPS=50
840 gross 836 MAX_CHANGE_OF_DT=2.
841 jgs 147
842     def __init__(self, models=[], **kwargs):
843 jgs 149 """
844     Initiates a simulation from a list of models.
845     """
846 gross 918 super(Simulation, self).__init__(**kwargs)
847 jgs 147 self.__models=[]
848 jgs 149
849 jgs 147 for i in range(len(models)):
850     self[i] = models[i]
851 jgs 149
852 jgs 121
853 jgs 147 def __repr__(self):
854     """
855 jgs 149 Returns a string representation of the Simulation.
856 jgs 147 """
857     return "<Simulation %r>" % self.__models
858 jgs 121
859 jgs 147 def __str__(self):
860     """
861 jgs 149 Returning Simulation as a string.
862 jgs 147 """
863     return "<Simulation %d>"%id(self)
864    
865     def iterModels(self):
866 jgs 149 """
867     Returns an iterator over the models.
868     """
869 jgs 147 return self.__models
870    
871     def __getitem__(self,i):
872 jgs 149 """
873     Returns the i-th model.
874     """
875 jgs 147 return self.__models[i]
876    
877     def __setitem__(self,i,value):
878 jgs 149 """
879     Sets the i-th model.
880     """
881 jgs 147 if not isinstance(value,Model):
882 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
883 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
884     self.__models.append(None)
885 jgs 147 self.__models[i]=value
886    
887     def __len__(self):
888 jgs 149 """
889     Returns the number of models.
890     """
891 jgs 147 return len(self.__models)
892 jgs 121
893 jgs 147
894     def getSafeTimeStepSize(self,dt):
895 jgs 149 """
896     Returns a time step size which can safely be used by all models.
897    
898     This is the minimum over the time step sizes of all models.
899     """
900 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
901     return out
902    
903     def doInitialization(self):
904 jgs 149 """
905     Initializes all models.
906     """
907 jgs 147 self.n=0
908     self.tn=0.
909     for o in self.iterModels():
910 gross 906 o.doInitialization()
911     def doInitialStep(self):
912     """
913     performs an iteration step in the initialization step for all models
914     """
915 gross 908 iter=0
916     while not self.terminateInitialIteration():
917     if iter==0: self.trace("iteration for initialization starts")
918     iter+=1
919     self.trace("iteration step %d"%(iter))
920     for o in self.iterModels():
921     o.doInitialStep()
922     if iter>self.MAX_ITER_STEPS:
923     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
924     self.trace("Initialization finalized after %s iteration steps."%iter)
925 gross 906
926     def doInitialPostprocessing(self):
927     """
928     finalises the initialization iteration process for all models.
929     """
930     for o in self.iterModels():
931     o.doInitialPostprocessing()
932 jgs 147 def finalize(self):
933 jgs 149 """
934     Returns True if any of the models is to be finalized.
935     """
936 jgs 147 return any([o.finalize() for o in self.iterModels()])
937 jgs 123
938 jgs 147 def doFinalization(self):
939 jgs 149 """
940 gross 906 finalises the time stepping for all models.
941 jgs 149 """
942 jgs 147 for i in self.iterModels(): i.doFinalization()
943     self.trace("end of time integation.")
944    
945     def doStepPreprocessing(self,dt):
946 jgs 149 """
947     Initializes the time step for all models.
948     """
949 jgs 147 for o in self.iterModels():
950     o.doStepPreprocessing(dt)
951    
952     def terminateIteration(self):
953 jgs 149 """
954     Returns True if all iterations for all models are terminated.
955     """
956 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
957     return out
958 gross 906
959     def terminateInitialIteration(self):
960     """
961     Returns True if all initial iterations for all models are terminated.
962     """
963     out=all([o.terminateInitialIteration() for o in self.iterModels()])
964     return out
965 jgs 123
966 jgs 147 def doStepPostprocessing(self,dt):
967 jgs 149 """
968 gross 906 finalises the iteration process for all models.
969 jgs 149 """
970 jgs 147 for o in self.iterModels():
971     o.doStepPostprocessing(dt)
972     self.n+=1
973     self.tn+=dt
974    
975     def doStep(self,dt):
976     """
977 jgs 149 Executes the iteration step at a time step for all model::
978 jgs 147
979 jgs 149 self.doStepPreprocessing(dt)
980     while not self.terminateIteration():
981     for all models:
982     self.doStep(dt)
983     self.doStepPostprocessing(dt)
984 jgs 147 """
985     self.iter=0
986     while not self.terminateIteration():
987     if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
988     self.iter+=1
989     self.trace("iteration step %d"%(self.iter))
990     for o in self.iterModels():
991     o.doStep(dt)
992     if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
993 jgs 121
994 jgs 147 def run(self,check_point=None):
995     """
996 jgs 149 Run the simulation by performing essentially::
997 jgs 123
998 jgs 149 self.doInitialization()
999 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
1000     self.doInitialPostprocessing()
1001 jgs 149 while not self.finalize():
1002     dt=self.getSafeTimeStepSize()
1003     self.doStep(dt)
1004     if n%check_point==0:
1005     self.writeXML()
1006     self.doFinalization()
1007 jgs 121
1008 jgs 149 If one of the models in throws a C{FailedTimeStepError} exception a
1009     new time step size is computed through getSafeTimeStepSize() and the
1010     time step is repeated.
1011 jgs 121
1012 jgs 149 If one of the models in throws a C{IterationDivergenceError}
1013     exception the time step size is halved and the time step is repeated.
1014 jgs 121
1015 jgs 149 In both cases the time integration is given up after
1016     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1017 jgs 147 """
1018 gross 906 self.doInitialization()
1019 gross 908 self.doInitialStep()
1020 gross 906 self.doInitialPostprocessing()
1021 jgs 147 dt=self.UNDEF_DT
1022     while not self.finalize():
1023     step_fail_counter=0
1024     iteration_fail_counter=0
1025 gross 836 if self.n==0:
1026     dt_new=self.getSafeTimeStepSize(dt)
1027     else:
1028 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1029 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1030     end_of_step=False
1031     while not end_of_step:
1032     end_of_step=True
1033     if not dt_new>0:
1034 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1035 jgs 147 try:
1036     self.doStepPreprocessing(dt_new)
1037     self.doStep(dt_new)
1038     self.doStepPostprocessing(dt_new)
1039     except IterationDivergenceError:
1040     dt_new*=0.5
1041     end_of_step=False
1042     iteration_fail_counter+=1
1043     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1044 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1045     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1046 jgs 147 except FailedTimeStepError:
1047     dt_new=self.getSafeTimeStepSize(dt)
1048     end_of_step=False
1049     step_fail_counter+=1
1050 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1051 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1052 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1053 jgs 147 dt=dt_new
1054     if not check_point==None:
1055     if n%check_point==0:
1056     self.trace("check point is created.")
1057     self.writeXML()
1058     self.doFinalization()
1059 jgs 121
1060 gross 918
1061     def toDom(self, esysxml, node):
1062     """
1063     C{toDom} method of Simulation class.
1064     """
1065     simulation = esysxml.createElement('Simulation')
1066     esysxml.registerLinkableObject(self, simulation)
1067     for rank, sim in enumerate(self.iterModels()):
1068     component = esysxml.createElement('Component')
1069     component.setAttribute('rank', str(rank))
1070     sim.toDom(esysxml, component)
1071     simulation.appendChild(component)
1072     node.appendChild(simulation)
1073    
1074    
1075     def fromDom(cls, esysxml, node):
1076 jgs 147 sims = []
1077 gross 918 for n in node.childNodes:
1078     if isinstance(n, minidom.Text):
1079 jgs 147 continue
1080 gross 918 sims.append(esysxml.getComponent(n))
1081     sims.sort(cmp=_comp)
1082     sim=cls([s[1] for s in sims], debug=esysxml.debug)
1083     esysxml.registerLinkableObject(sim, node)
1084     return sim
1085 jgs 121
1086 jgs 147 fromDom = classmethod(fromDom)
1087 jgs 121
1088 gross 918 def _comp(a,b):
1089     if a[0]<a[1]:
1090     return 1
1091     elif a[0]>a[1]:
1092     return -1
1093     else:
1094     return 0
1095 jgs 121
1096 jgs 147 class IterationDivergenceError(Exception):
1097     """
1098 jgs 149 Exception which is thrown if there is no convergence of the iteration
1099     process at a time step.
1100 jgs 121
1101 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1102 jgs 147 """
1103     pass
1104 jgs 121
1105 jgs 147 class FailedTimeStepError(Exception):
1106 jgs 149 """
1107     Exception which is thrown if the time step fails because of a step
1108     size that have been choosen to be too large.
1109     """
1110 jgs 147 pass
1111 jgs 121
1112 jgs 147 class SimulationBreakDownError(Exception):
1113 jgs 149 """
1114     Exception which is thrown if the simulation does not manage to
1115     progress in time.
1116     """
1117 jgs 147 pass
1118 jgs 121
1119 jgs 147 class NonPositiveStepSizeError(Exception):
1120 jgs 149 """
1121     Exception which is thrown if the step size is not positive.
1122     """
1123 jgs 147 pass
1124 jgs 149
1125 elspeth 875 class DataSource(object):
1126     """
1127     Class for handling data sources, including local and remote files. This class is under development.
1128     """
1129    
1130 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1131 elspeth 875 self.uri = uri
1132     self.fileformat = fileformat
1133    
1134 gross 918 def toDom(self, esysxml, node):
1135 elspeth 875 """
1136     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1137 gross 918 current XML esysxml.
1138 elspeth 875 """
1139 gross 918 ds = esysxml.createElement('DataSource')
1140     ds.appendChild(esysxml.createDataNode('URI', self.uri))
1141     ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1142 elspeth 875 node.appendChild(ds)
1143    
1144 gross 918 def fromDom(cls, esysxml, node):
1145 gross 920 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1146     fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1147 elspeth 875 ds = cls(uri, fileformat)
1148     return ds
1149    
1150 gross 885 def getLocalFileName(self):
1151     return self.uri
1152    
1153 elspeth 875 fromDom = classmethod(fromDom)
1154    
1155 jgs 149 # vim: expandtab shiftwidth=4:

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.26