/[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 952 - (hide annotations)
Wed Feb 7 23:53:24 2007 UTC (12 years, 8 months ago) by gross
File MIME type: text/x-python
File size: 40650 byte(s)
testing of simulation completness improved.
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 gross 944 self.__esys=self.__dom.getElementsByTagName('ESys')[0]
75 gross 918 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 gross 938 def getTarget(self):
218     """
219     returns the target
220     """
221     return self.target
222 gross 912 def getAttributeName(self):
223     """
224     returns the name of the attribute the link is pointing to
225     """
226     return self.attribute
227 jgs 123
228     def setAttributeName(self,attribute):
229 jgs 126 """
230 jgs 149 Set a new attribute name to be collected from the target object. The
231 jgs 126 target object must have the attribute with name attribute.
232     """
233 jgs 147 if attribute and self.target:
234     if isinstance(self.target,LinkableObject):
235     if not self.target.hasAttribute(attribute):
236     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
237     else:
238     if not hasattr(self.target,attribute):
239     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
240 jgs 123 self.attribute = attribute
241    
242     def hasDefinedAttributeName(self):
243 jgs 126 """
244 jgs 149 Returns true if an attribute name is set.
245 jgs 126 """
246 jgs 123 return self.attribute != None
247    
248     def __repr__(self):
249 jgs 126 """
250 jgs 149 Returns a string representation of the link.
251 jgs 126 """
252 jgs 123 if self.hasDefinedAttributeName():
253     return "<Link to attribute %s of %s>" % (self.attribute, self.target)
254     else:
255     return "<Link to target %s>" % self.target
256    
257     def __call__(self,name=None):
258 jgs 126 """
259 jgs 149 Returns the value of the attribute of the target object. If the
260 jgs 126 atrribute is callable then the return value of the call is returned.
261     """
262 jgs 123 if name:
263     out=getattr(self.target, name)
264     else:
265     out=getattr(self.target, self.attribute)
266 jgs 121
267 jgs 123 if callable(out):
268     return out()
269     else:
270     return out
271 jgs 121
272 gross 918 def toDom(self, esysxml, node):
273 jgs 126 """
274 jgs 149 C{toDom} method of Link. Creates a Link node and appends it to the
275 gross 918 current XML esysxml.
276 jgs 126 """
277 gross 918 link = esysxml.createElement('Link')
278 jgs 147 assert (self.target != None), ("Target was none, name was %r" % self.attribute)
279 gross 918 link.appendChild(esysxml.createDataNode('Target', esysxml.getLinkableObjectId(self.target)))
280 jgs 123 # this use of id will not work for purposes of being able to retrieve the intended
281     # target from the xml later. I need a better unique identifier.
282     assert self.attribute, "You can't xmlify a Link without a target attribute"
283 gross 918 link.appendChild(esysxml.createDataNode('Attribute', self.attribute))
284 jgs 123 node.appendChild(link)
285 jgs 121
286 gross 918 def fromDom(cls, esysxml, node):
287     targetid = int(node.getElementsByTagName("Target")[0].firstChild.nodeValue.strip())
288     attribute =str(node.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip())
289 jgs 123 l = cls(None, attribute)
290 gross 918 esysxml.registerLink(targetid, l)
291 jgs 123 return l
292 jgs 121
293 jgs 123 fromDom = classmethod(fromDom)
294    
295 jgs 122 class LinkableObject(object):
296 jgs 123 """
297     An object that allows to link its attributes to attributes of other objects
298 jgs 149 via a Link object. For instance::
299 jgs 123
300     p = LinkableObject()
301     p.x = Link(o,"name")
302     print p.x
303    
304 jgs 149 links attribute C{x} of C{p} to the attribute name of object C{o}.
305 jgs 121
306 jgs 149 C{p.x} will contain the current value of attribute C{name} of object
307     C{o}.
308 jgs 121
309 jgs 149 If the value of C{getattr(o, "name")} is callable, C{p.x} will return
310     the return value of the call.
311 jgs 123 """
312    
313    
314 gross 918 def __init__(self, id = None, debug=False):
315 jgs 149 """
316     Initializes LinkableObject so that we can operate on Links
317     """
318 jgs 123 self.debug = debug
319     self.__linked_attributes={}
320 gross 918
321 jgs 123 def trace(self, msg):
322     """
323 jgs 149 If debugging is on, print the message, otherwise do nothing
324     """
325 jgs 123 if self.debug:
326 jgs 147 print "%s: %s"%(str(self),msg)
327 jgs 123
328     def __getattr__(self,name):
329 jgs 149 """
330     Returns the value of attribute name. If the value is a Link object the
331     object is called and the return value is returned.
332     """
333 jgs 123 out = self.getAttributeObject(name)
334     if isinstance(out,Link):
335     return out()
336     else:
337     return out
338    
339     def getAttributeObject(self,name):
340 jgs 149 """
341     Return the object stored for attribute name.
342     """
343 jgs 123
344     if self.__dict__.has_key(name):
345     return self.__dict__[name]
346    
347     if self.__linked_attributes.has_key(name):
348     return self.__linked_attributes[name]
349    
350 jgs 147 if self.__class__.__dict__.has_key(name):
351     return self.__class.__dict__[name]
352    
353 jgs 123 raise AttributeError,"No attribute %s."%name
354    
355 jgs 147 def hasAttribute(self,name):
356 jgs 149 """
357     Returns True if self as attribute name.
358     """
359 jgs 147 return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name)
360    
361 jgs 123 def __setattr__(self,name,value):
362 jgs 149 """
363     Sets the value for attribute name. If value is a Link the target
364     attribute is set to name if no attribute has been specified.
365     """
366 jgs 123
367     if self.__dict__.has_key(name):
368     del self.__dict__[name]
369    
370     if isinstance(value,Link):
371     if not value.hasDefinedAttributeName():
372     value.setAttributeName(name)
373     self.__linked_attributes[name] = value
374    
375 jgs 147 self.trace("attribute %s is now linked by %s."%(name,value))
376 jgs 123 else:
377     self.__dict__[name] = value
378    
379     def __delattr__(self,name):
380 jgs 149 """
381     Removes the attribute name.
382     """
383 jgs 123
384     if self.__linked_attributes.has_key[name]:
385     del self.__linked_attributes[name]
386     elif self.__dict__.has_key(name):
387     del self.__dict__[name]
388     else:
389     raise AttributeError,"No attribute %s."%name
390    
391 jgs 122 class _ParameterIterator:
392 jgs 123 def __init__(self,parameterset):
393 jgs 121
394 jgs 123 self.__set=parameterset
395     self.__iter=iter(parameterset.parameters)
396    
397     def next(self):
398     o=self.__iter.next()
399     return (o,self.__set.getAttributeObject(o))
400    
401     def __iter__(self):
402     return self
403    
404 jgs 122 class ParameterSet(LinkableObject):
405 jgs 149 """
406     A class which allows to emphazise attributes to be written and read to XML
407 jgs 123
408 jgs 149 Leaves of an ESySParameters object can be:
409 jgs 123
410 jgs 149 - a real number
411     - a integer number
412     - a string
413     - a boolean value
414     - a ParameterSet object
415     - a Simulation object
416     - a Model object
417 elspeth 874 - a numarray object
418     - a list of booleans
419     - any other object (not considered by writeESySXML and writeXML)
420 jgs 123
421 jgs 149 Example how to create an ESySParameters object::
422 jgs 123
423 jgs 149 p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.)
424     p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11)
425     parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1")))
426 jgs 123
427 jgs 149 This can be accessed as::
428 jgs 123
429 jgs 149 parm.parm1.gamma=0.
430     parm.parm1.dim=2
431     parm.parm1.tol_v=0.001
432     parm.parm1.output_file="/tmp/u.%3.3d.dx"
433     parm.parm1.runFlag=True
434     parm.parm1.parm11.gamma1=1.
435     parm.parm1.parm11.gamma2=2.
436     parm.parm1.parm11.gamma3=3.
437     parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1)
438 jgs 123 """
439     def __init__(self, parameters=[], **kwargs):
440 jgs 149 """
441     Creates a ParameterSet with parameters parameters.
442     """
443 jgs 123 LinkableObject.__init__(self, **kwargs)
444     self.parameters = set()
445     self.declareParameters(parameters)
446 jgs 147
447     def __repr__(self):
448 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
449 jgs 123
450     def declareParameter(self,**parameters):
451 jgs 149 """
452     Declares a new parameter(s) and its (their) initial value.
453     """
454 jgs 123 self.declareParameters(parameters)
455    
456     def declareParameters(self,parameters):
457 jgs 149 """
458     Declares a set of parameters. parameters can be a list, a dictionary
459     or a ParameterSet.
460     """
461 jgs 123 if isinstance(parameters,ListType):
462     parameters = zip(parameters, itertools.repeat(None))
463     if isinstance(parameters,DictType):
464     parameters = parameters.iteritems()
465 jgs 121
466 jgs 123 for prm, value in parameters:
467     setattr(self,prm,value)
468     self.parameters.add(prm)
469 jgs 121
470 jgs 147 self.trace("parameter %s has been declared."%prm)
471 jgs 121
472 jgs 123 def releaseParameters(self,name):
473 jgs 149 """
474     Removes parameter name from the paramameters.
475     """
476 jgs 123 if self.isParameter(name):
477     self.parameters.remove(name)
478 jgs 147 self.trace("parameter %s has been removed."%name)
479 gross 950
480     def checkLinkTargets(self, models, hash):
481     """
482 gross 952 returns a set of tuples ("<self>(<name>)", <target model>) if the parameter <name> is linked to model <target model>
483     but <target model> is not in the list models. If the a parameter is linked to another parameter set which is not in the hash list
484     the parameter set is checked for its models. hash gives the call history.
485 gross 950 """
486 gross 952 out=set()
487 gross 950 for name, value in self:
488     if isinstance(value, Link):
489     m=value.getTarget()
490 gross 952 if isinstance(m, Model):
491     if not m in models: out.add( (str(self)+"("+name+")",m) )
492     elif isinstance(m, ParameterSet) and not m in hash:
493     out|=set( [ (str(self)+"("+name+")."+f[0],f[1]) for f in m.checkLinkTargets(models, hash+[ self ] ) ] )
494     return out
495 jgs 123
496     def __iter__(self):
497 jgs 149 """
498     Creates an iterator over the parameter and their values.
499     """
500 jgs 123 return _ParameterIterator(self)
501    
502     def showParameters(self):
503 jgs 149 """
504     Returns a descrition of the parameters.
505     """
506 jgs 123 out="{"
507     notfirst=False
508     for i,v in self:
509     if notfirst: out=out+","
510     notfirst=True
511     if isinstance(v,ParameterSet):
512     out="%s\"%s\" : %s"%(out,i,v.showParameters())
513     else:
514     out="%s\"%s\" : %s"%(out,i,v)
515     return out+"}"
516    
517     def __delattr__(self,name):
518 jgs 149 """
519     Removes the attribute name.
520     """
521 jgs 123 LinkableObject.__delattr__(self,name)
522     try:
523     self.releaseParameter(name)
524     except:
525     pass
526 jgs 121
527 gross 918 def toDom(self, esysxml, node):
528 jgs 149 """
529 gross 918 C{toDom} method of Model class
530 jgs 149 """
531 gross 918 pset = esysxml.createElement('ParameterSet')
532     pset.setAttribute('type', self.__class__.__name__)
533     pset.setAttribute('module', self.__class__.__module__)
534     esysxml.registerLinkableObject(self, pset)
535     self._parametersToDom(esysxml, pset)
536 jgs 123 node.appendChild(pset)
537 jgs 121
538 gross 918 def _parametersToDom(self, esysxml, node):
539 jgs 123 for name,value in self:
540 gross 918 # convert list to numarray when possible:
541     if isinstance (value, list):
542     elem_type=-1
543     for i in value:
544     if isinstance(i, bool):
545     elem_type = max(elem_type,0)
546     if isinstance(i, int) and not isinstance(i, bool):
547     elem_type = max(elem_type,1)
548     if isinstance(i, float):
549     elem_type = max(elem_type,2)
550     if elem_type == 0: value = numarray.array(value,numarray.Bool)
551     if elem_type == 1: value = numarray.array(value,numarray.Int)
552     if elem_type == 2: value = numarray.array(value,numarray.Float)
553    
554     param = esysxml.createElement('Parameter')
555 jgs 123 param.setAttribute('type', value.__class__.__name__)
556 jgs 121
557 gross 918 param.appendChild(esysxml.createDataNode('Name', name))
558 jgs 121
559 gross 918 val = esysxml.createElement('Value')
560 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
561 gross 918 value.toDom(esysxml, val)
562 jgs 123 param.appendChild(val)
563 elspeth 871 elif isinstance(value, numarray.NumArray):
564     shape = value.getshape()
565     if isinstance(shape, tuple):
566     size = reduce(operator.mul, shape)
567     shape = ' '.join(map(str, shape))
568     else:
569     size = shape
570     shape = str(shape)
571    
572     arraytype = value.type()
573 gross 918 if isinstance(arraytype, numarray.BooleanType):
574     arraytype_str="Bool"
575     elif isinstance(arraytype, numarray.IntegralType):
576     arraytype_str="Int"
577     elif isinstance(arraytype, numarray.FloatingType):
578     arraytype_str="Float"
579     elif isinstance(arraytype, numarray.ComplexType):
580     arraytype_str="Complex"
581     else:
582     arraytype_str=str(arraytype)
583     numarrayElement = esysxml.createElement('NumArray')
584     numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
585     numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
586     numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
587 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
588 elspeth 874 val.appendChild(numarrayElement)
589 elspeth 871 param.appendChild(val)
590 gross 918 elif isinstance(value, list):
591     param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
592     elif isinstance(value, (str, bool, int, float, type(None))):
593     param.appendChild(esysxml.createDataNode('Value', str(value)))
594 gross 926 elif isinstance(value, dict):
595     dic = esysxml.createElement('dictionary')
596     if len(value.keys())>0:
597     dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
598     dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
599     for k,v in value.items():
600     i=esysxml.createElement('item')
601     i.appendChild(esysxml.createDataNode('key', k))
602     i.appendChild(esysxml.createDataNode('value', v))
603     dic.appendChild(i)
604     param.appendChild(dic)
605 jgs 123 else:
606 gross 918 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
607 jgs 123
608     node.appendChild(param)
609    
610 gross 918 def fromDom(cls, esysxml, node):
611 jgs 123 # Define a host of helper functions to assist us.
612     def _children(node):
613     """
614 jgs 149 Remove the empty nodes from the children of this node.
615 jgs 123 """
616 elspeth 871 ret = []
617     for x in node.childNodes:
618     if isinstance(x, minidom.Text):
619     if x.nodeValue.strip():
620     ret.append(x)
621     else:
622     ret.append(x)
623     return ret
624 jgs 123
625 gross 918 def _floatfromValue(esysxml, node):
626     return float(node.nodeValue.strip())
627 jgs 123
628 gross 918 def _stringfromValue(esysxml, node):
629     return str(node.nodeValue.strip())
630 jgs 126
631 gross 918 def _intfromValue(esysxml, node):
632     return int(node.nodeValue.strip())
633 jgs 126
634 gross 918 def _boolfromValue(esysxml, node):
635     return _boolfromstring(node.nodeValue.strip())
636 elspeth 266
637 gross 918 def _nonefromValue(esysxml, node):
638 elspeth 266 return None
639 elspeth 871
640 gross 918 def _numarrayfromValue(esysxml, node):
641     for node in _children(node):
642 elspeth 871 if node.tagName == 'ArrayType':
643     arraytype = node.firstChild.nodeValue.strip()
644     if node.tagName == 'Shape':
645     shape = node.firstChild.nodeValue.strip()
646     shape = [int(x) for x in shape.split()]
647     if node.tagName == 'Data':
648     data = node.firstChild.nodeValue.strip()
649     data = [float(x) for x in data.split()]
650     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
651     shape)
652 elspeth 874
653 gross 918 def _listfromValue(esysxml, node):
654     return [x for x in node.nodeValue.split()]
655 elspeth 874
656     def _boolfromstring(s):
657     if s == 'True':
658     return True
659     else:
660     return False
661 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
662     ptypemap = {"Simulation": Simulation.fromDom,
663     "Model":Model.fromDom,
664     "ParameterSet":ParameterSet.fromDom,
665     "Link":Link.fromDom,
666 elspeth 875 "DataSource":DataSource.fromDom,
667 jgs 123 "float":_floatfromValue,
668 jgs 126 "int":_intfromValue,
669 jgs 123 "str":_stringfromValue,
670 elspeth 269 "bool":_boolfromValue,
671 elspeth 874 "list":_listfromValue,
672     "NumArray":_numarrayfromValue,
673 elspeth 871 "NoneType":_nonefromValue,
674 jgs 123 }
675    
676     parameters = {}
677 gross 918 for n in _children(node):
678     ptype = n.getAttribute("type")
679     if not ptypemap.has_key(ptype):
680     raise KeyError("cannot handle parameter type %s."%ptype)
681 jgs 123
682     pname = pvalue = None
683 gross 918 for childnode in _children(n):
684 jgs 123 if childnode.tagName == "Name":
685     pname = childnode.firstChild.nodeValue.strip()
686    
687     if childnode.tagName == "Value":
688     nodes = _children(childnode)
689 gross 918 pvalue = ptypemap[ptype](esysxml, nodes[0])
690 jgs 123
691     parameters[pname] = pvalue
692    
693     # Create the instance of ParameterSet
694 gross 918 o = cls(debug=esysxml.debug)
695 jgs 123 o.declareParameters(parameters)
696 gross 918 esysxml.registerLinkableObject(o, node)
697 jgs 123 return o
698    
699     fromDom = classmethod(fromDom)
700 gross 918
701 jgs 123 def writeXML(self,ostream=stdout):
702 jgs 149 """
703     Writes the object as an XML object into an output stream.
704     """
705 gross 918 esysxml=ESySXMLCreator()
706     self.toDom(esysxml, esysxml.getRoot())
707     ostream.write(esysxml.toprettyxml())
708    
709 jgs 147 class Model(ParameterSet):
710     """
711 jgs 149 A Model object represents a processess marching over time until a
712     finalizing condition is fullfilled. At each time step an iterative
713     process can be performed and the time step size can be controlled. A
714     Model has the following work flow::
715 gross 906
716 jgs 147 doInitialization()
717 gross 906 while not terminateInitialIteration(): doInitializationiStep()
718     doInitialPostprocessing()
719 jgs 147 while not finalize():
720     dt=getSafeTimeStepSize(dt)
721     doStepPreprocessing(dt)
722     while not terminateIteration(): doStep(dt)
723     doStepPostprocessing(dt)
724     doFinalization()
725 jgs 121
726 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
727     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
728     C{doFinalization} are methods of the particular instance of a Model. The
729     default implementations of these methods have to be overwritten by the
730     subclass implementing a Model.
731 jgs 147 """
732 jgs 121
733 jgs 147 UNDEF_DT=1.e300
734 jgs 121
735 gross 918 def __init__(self,parameters=[],**kwargs):
736 jgs 149 """
737     Creates a model.
738 jgs 121
739 jgs 149 Just calls the parent constructor.
740 jgs 147 """
741 gross 918 ParameterSet.__init__(self, parameters=parameters,**kwargs)
742 jgs 121
743 jgs 147 def __str__(self):
744 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
745 jgs 121
746    
747 jgs 147 def doInitialization(self):
748 jgs 149 """
749     Initializes the time stepping scheme.
750    
751     This function may be overwritten.
752     """
753 jgs 147 pass
754 gross 906 def doInitialStep(self):
755     """
756     performs an iteration step in the initialization phase
757    
758     This function may be overwritten.
759     """
760     pass
761    
762     def terminateInitialIteration(self):
763     """
764     Returns True if iteration at the inital phase is terminated.
765     """
766     return True
767    
768     def doInitialPostprocessing(self):
769     """
770     finalises the initialization iteration process
771    
772     This function may be overwritten.
773     """
774     pass
775 jgs 147
776     def getSafeTimeStepSize(self,dt):
777 jgs 149 """
778     Returns a time step size which can safely be used.
779    
780     C{dt} gives the previously used step size.
781    
782     This function may be overwritten.
783     """
784 jgs 147 return self.UNDEF_DT
785    
786     def finalize(self):
787 jgs 149 """
788     Returns False if the time stepping is finalized.
789    
790     This function may be overwritten.
791     """
792 jgs 147 return False
793 jgs 123
794 jgs 147 def doFinalization(self):
795 jgs 149 """
796     Finalizes the time stepping.
797    
798     This function may be overwritten.
799     """
800 jgs 147 pass
801    
802     def doStepPreprocessing(self,dt):
803 jgs 149 """
804     Sets up a time step of step size dt.
805    
806     This function may be overwritten.
807     """
808 jgs 147 pass
809    
810     def doStep(self,dt):
811 jgs 149 """
812     Executes an iteration step at a time step.
813    
814     C{dt} is the currently used time step size.
815    
816     This function may be overwritten.
817     """
818 jgs 147 pass
819    
820     def terminateIteration(self):
821 jgs 149 """
822     Returns True if iteration on a time step is terminated.
823     """
824 jgs 147 return True
825 gross 906
826 jgs 147
827     def doStepPostprocessing(self,dt):
828 jgs 149 """
829 gross 906 finalises the time step.
830 jgs 149
831     dt is the currently used time step size.
832    
833     This function may be overwritten.
834     """
835 jgs 147 pass
836 gross 918
837     def toDom(self, esysxml, node):
838     """
839     C{toDom} method of Model class
840     """
841     pset = esysxml.createElement('Model')
842     pset.setAttribute('type', self.__class__.__name__)
843     pset.setAttribute('module', self.__class__.__module__)
844     esysxml.registerLinkableObject(self, pset)
845     node.appendChild(pset)
846     self._parametersToDom(esysxml, pset)
847 jgs 147
848     class Simulation(Model):
849     """
850 jgs 149 A Simulation object is special Model which runs a sequence of Models.
851 jgs 121
852 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
853     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
854     C{doFinalization} are executing the corresponding methods of the models in
855     the simulation.
856 jgs 147 """
857    
858     FAILED_TIME_STEPS_MAX=20
859     MAX_ITER_STEPS=50
860 gross 836 MAX_CHANGE_OF_DT=2.
861 jgs 147
862     def __init__(self, models=[], **kwargs):
863 jgs 149 """
864     Initiates a simulation from a list of models.
865     """
866 gross 918 super(Simulation, self).__init__(**kwargs)
867 gross 938 for m in models:
868     if not isinstance(m, Model):
869     raise TypeError("%s is not a subclass of Model."%m)
870 jgs 147 self.__models=[]
871     for i in range(len(models)):
872     self[i] = models[i]
873 jgs 149
874 jgs 121
875 jgs 147 def __repr__(self):
876     """
877 jgs 149 Returns a string representation of the Simulation.
878 jgs 147 """
879     return "<Simulation %r>" % self.__models
880 jgs 121
881 jgs 147 def __str__(self):
882     """
883 jgs 149 Returning Simulation as a string.
884 jgs 147 """
885     return "<Simulation %d>"%id(self)
886    
887     def iterModels(self):
888 jgs 149 """
889     Returns an iterator over the models.
890     """
891 jgs 147 return self.__models
892    
893     def __getitem__(self,i):
894 jgs 149 """
895     Returns the i-th model.
896     """
897 jgs 147 return self.__models[i]
898    
899     def __setitem__(self,i,value):
900 jgs 149 """
901     Sets the i-th model.
902     """
903 jgs 147 if not isinstance(value,Model):
904 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
905 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
906     self.__models.append(None)
907 jgs 147 self.__models[i]=value
908    
909     def __len__(self):
910 jgs 149 """
911     Returns the number of models.
912     """
913 jgs 147 return len(self.__models)
914 jgs 121
915 gross 938 def getAllModels(self):
916     """
917     returns a list of all models used in the Simulation including subsimulations
918     """
919     out=[]
920     for m in self.iterModels():
921     if isinstance(m, Simulation):
922     out+=m.getAllModels()
923     else:
924     out.append(m)
925     return list(set(out))
926    
927 gross 950 def checkModels(self, models, hash):
928 gross 938 """
929     returns a list of (model,parameter, target model ) if the the parameter of model
930     is linking to the target_model which is not in list of models.
931     """
932 gross 952 out=self.checkLinkTargets(models, hash + [self])
933 gross 938 for m in self.iterModels():
934     if isinstance(m, Simulation):
935 gross 952 out|=m.checkModels(models, hash)
936 gross 938 else:
937 gross 952 out|=m.checkLinkTargets(models, hash + [self])
938     return set( [ (str(self)+"."+f[0],f[1]) for f in out ] )
939 gross 938
940 jgs 147
941     def getSafeTimeStepSize(self,dt):
942 jgs 149 """
943     Returns a time step size which can safely be used by all models.
944    
945     This is the minimum over the time step sizes of all models.
946     """
947 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
948     return out
949    
950     def doInitialization(self):
951 jgs 149 """
952     Initializes all models.
953     """
954 jgs 147 self.n=0
955     self.tn=0.
956     for o in self.iterModels():
957 gross 906 o.doInitialization()
958     def doInitialStep(self):
959     """
960     performs an iteration step in the initialization step for all models
961     """
962 gross 908 iter=0
963     while not self.terminateInitialIteration():
964     if iter==0: self.trace("iteration for initialization starts")
965     iter+=1
966     self.trace("iteration step %d"%(iter))
967     for o in self.iterModels():
968     o.doInitialStep()
969     if iter>self.MAX_ITER_STEPS:
970     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
971     self.trace("Initialization finalized after %s iteration steps."%iter)
972 gross 906
973     def doInitialPostprocessing(self):
974     """
975     finalises the initialization iteration process for all models.
976     """
977     for o in self.iterModels():
978     o.doInitialPostprocessing()
979 jgs 147 def finalize(self):
980 jgs 149 """
981     Returns True if any of the models is to be finalized.
982     """
983 jgs 147 return any([o.finalize() for o in self.iterModels()])
984 jgs 123
985 jgs 147 def doFinalization(self):
986 jgs 149 """
987 gross 906 finalises the time stepping for all models.
988 jgs 149 """
989 jgs 147 for i in self.iterModels(): i.doFinalization()
990     self.trace("end of time integation.")
991    
992     def doStepPreprocessing(self,dt):
993 jgs 149 """
994     Initializes the time step for all models.
995     """
996 jgs 147 for o in self.iterModels():
997     o.doStepPreprocessing(dt)
998    
999     def terminateIteration(self):
1000 jgs 149 """
1001     Returns True if all iterations for all models are terminated.
1002     """
1003 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
1004     return out
1005 gross 906
1006     def terminateInitialIteration(self):
1007     """
1008     Returns True if all initial iterations for all models are terminated.
1009     """
1010     out=all([o.terminateInitialIteration() for o in self.iterModels()])
1011     return out
1012 jgs 123
1013 jgs 147 def doStepPostprocessing(self,dt):
1014 jgs 149 """
1015 gross 906 finalises the iteration process for all models.
1016 jgs 149 """
1017 jgs 147 for o in self.iterModels():
1018     o.doStepPostprocessing(dt)
1019     self.n+=1
1020     self.tn+=dt
1021    
1022     def doStep(self,dt):
1023     """
1024 jgs 149 Executes the iteration step at a time step for all model::
1025 jgs 147
1026 jgs 149 self.doStepPreprocessing(dt)
1027     while not self.terminateIteration():
1028     for all models:
1029     self.doStep(dt)
1030     self.doStepPostprocessing(dt)
1031 jgs 147 """
1032     self.iter=0
1033     while not self.terminateIteration():
1034     if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
1035     self.iter+=1
1036     self.trace("iteration step %d"%(self.iter))
1037     for o in self.iterModels():
1038     o.doStep(dt)
1039     if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
1040 jgs 121
1041 jgs 147 def run(self,check_point=None):
1042     """
1043 jgs 149 Run the simulation by performing essentially::
1044 jgs 123
1045 jgs 149 self.doInitialization()
1046 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
1047     self.doInitialPostprocessing()
1048 jgs 149 while not self.finalize():
1049     dt=self.getSafeTimeStepSize()
1050     self.doStep(dt)
1051     if n%check_point==0:
1052     self.writeXML()
1053     self.doFinalization()
1054 jgs 121
1055 jgs 149 If one of the models in throws a C{FailedTimeStepError} exception a
1056     new time step size is computed through getSafeTimeStepSize() and the
1057     time step is repeated.
1058 jgs 121
1059 jgs 149 If one of the models in throws a C{IterationDivergenceError}
1060     exception the time step size is halved and the time step is repeated.
1061 jgs 121
1062 jgs 149 In both cases the time integration is given up after
1063     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1064 jgs 147 """
1065 gross 938 # check the completness of the models:
1066     # first a list of all the models involved in the simulation including subsimulations:
1067     #
1068 gross 950 missing=self.checkModels(self.getAllModels(), [])
1069 gross 938 if len(missing)>0:
1070     msg=""
1071     for l in missing:
1072 gross 952 msg+="\n\t"+str(l[1])+" at "+l[0]
1073 gross 938 raise MissingLink("link targets missing in the Simulation: %s"%msg)
1074     #==============================
1075 gross 906 self.doInitialization()
1076 gross 908 self.doInitialStep()
1077 gross 906 self.doInitialPostprocessing()
1078 jgs 147 dt=self.UNDEF_DT
1079     while not self.finalize():
1080     step_fail_counter=0
1081     iteration_fail_counter=0
1082 gross 836 if self.n==0:
1083     dt_new=self.getSafeTimeStepSize(dt)
1084     else:
1085 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1086 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1087     end_of_step=False
1088     while not end_of_step:
1089     end_of_step=True
1090     if not dt_new>0:
1091 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1092 jgs 147 try:
1093     self.doStepPreprocessing(dt_new)
1094     self.doStep(dt_new)
1095     self.doStepPostprocessing(dt_new)
1096     except IterationDivergenceError:
1097     dt_new*=0.5
1098     end_of_step=False
1099     iteration_fail_counter+=1
1100     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1101 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1102     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1103 jgs 147 except FailedTimeStepError:
1104     dt_new=self.getSafeTimeStepSize(dt)
1105     end_of_step=False
1106     step_fail_counter+=1
1107 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1108 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1109 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1110 jgs 147 dt=dt_new
1111     if not check_point==None:
1112     if n%check_point==0:
1113     self.trace("check point is created.")
1114     self.writeXML()
1115     self.doFinalization()
1116 jgs 121
1117 gross 918
1118     def toDom(self, esysxml, node):
1119     """
1120     C{toDom} method of Simulation class.
1121     """
1122     simulation = esysxml.createElement('Simulation')
1123     esysxml.registerLinkableObject(self, simulation)
1124     for rank, sim in enumerate(self.iterModels()):
1125     component = esysxml.createElement('Component')
1126     component.setAttribute('rank', str(rank))
1127     sim.toDom(esysxml, component)
1128     simulation.appendChild(component)
1129     node.appendChild(simulation)
1130    
1131    
1132     def fromDom(cls, esysxml, node):
1133 jgs 147 sims = []
1134 gross 918 for n in node.childNodes:
1135     if isinstance(n, minidom.Text):
1136 jgs 147 continue
1137 gross 918 sims.append(esysxml.getComponent(n))
1138     sims.sort(cmp=_comp)
1139     sim=cls([s[1] for s in sims], debug=esysxml.debug)
1140     esysxml.registerLinkableObject(sim, node)
1141     return sim
1142 jgs 121
1143 jgs 147 fromDom = classmethod(fromDom)
1144 jgs 121
1145 gross 918 def _comp(a,b):
1146     if a[0]<a[1]:
1147     return 1
1148     elif a[0]>a[1]:
1149     return -1
1150     else:
1151     return 0
1152 jgs 121
1153 jgs 147 class IterationDivergenceError(Exception):
1154     """
1155 jgs 149 Exception which is thrown if there is no convergence of the iteration
1156     process at a time step.
1157 jgs 121
1158 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1159 jgs 147 """
1160     pass
1161 jgs 121
1162 jgs 147 class FailedTimeStepError(Exception):
1163 jgs 149 """
1164     Exception which is thrown if the time step fails because of a step
1165     size that have been choosen to be too large.
1166     """
1167 jgs 147 pass
1168 jgs 121
1169 jgs 147 class SimulationBreakDownError(Exception):
1170 jgs 149 """
1171     Exception which is thrown if the simulation does not manage to
1172     progress in time.
1173     """
1174 jgs 147 pass
1175 jgs 121
1176 jgs 147 class NonPositiveStepSizeError(Exception):
1177 jgs 149 """
1178     Exception which is thrown if the step size is not positive.
1179     """
1180 jgs 147 pass
1181 jgs 149
1182 gross 938 class MissingLink(Exception):
1183     """
1184     Exception thrown when a link is missing
1185     """
1186     pass
1187    
1188 elspeth 875 class DataSource(object):
1189     """
1190     Class for handling data sources, including local and remote files. This class is under development.
1191     """
1192    
1193 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1194 elspeth 875 self.uri = uri
1195     self.fileformat = fileformat
1196    
1197 gross 918 def toDom(self, esysxml, node):
1198 elspeth 875 """
1199     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1200 gross 918 current XML esysxml.
1201 elspeth 875 """
1202 gross 918 ds = esysxml.createElement('DataSource')
1203     ds.appendChild(esysxml.createDataNode('URI', self.uri))
1204     ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1205 elspeth 875 node.appendChild(ds)
1206    
1207 gross 918 def fromDom(cls, esysxml, node):
1208 gross 920 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1209     fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1210 elspeth 875 ds = cls(uri, fileformat)
1211     return ds
1212    
1213 gross 885 def getLocalFileName(self):
1214     return self.uri
1215    
1216 elspeth 875 fromDom = classmethod(fromDom)
1217    
1218 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