/[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 964 - (hide annotations)
Tue Feb 13 05:10:26 2007 UTC (12 years, 8 months ago) by gross
File MIME type: text/x-python
File size: 40562 byte(s)
The set/getRefVal functions of Data objects have been removed (mainly to avoid later problems with MPI).
Moreover, a faster access to the reference id of samples has been introduced. I don't think that anybody will
profit form this at this stage but it will allow a faster dump of data objects.


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 123 def releaseParameters(self,name):
471 jgs 149 """
472     Removes parameter name from the paramameters.
473     """
474 jgs 123 if self.isParameter(name):
475     self.parameters.remove(name)
476 jgs 147 self.trace("parameter %s has been removed."%name)
477 gross 950
478     def checkLinkTargets(self, models, hash):
479     """
480 gross 952 returns a set of tuples ("<self>(<name>)", <target model>) if the parameter <name> is linked to model <target model>
481     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
482     the parameter set is checked for its models. hash gives the call history.
483 gross 950 """
484 gross 952 out=set()
485 gross 950 for name, value in self:
486     if isinstance(value, Link):
487     m=value.getTarget()
488 gross 952 if isinstance(m, Model):
489     if not m in models: out.add( (str(self)+"("+name+")",m) )
490     elif isinstance(m, ParameterSet) and not m in hash:
491     out|=set( [ (str(self)+"("+name+")."+f[0],f[1]) for f in m.checkLinkTargets(models, hash+[ self ] ) ] )
492     return out
493 jgs 123
494     def __iter__(self):
495 jgs 149 """
496     Creates an iterator over the parameter and their values.
497     """
498 jgs 123 return _ParameterIterator(self)
499    
500     def showParameters(self):
501 jgs 149 """
502     Returns a descrition of the parameters.
503     """
504 jgs 123 out="{"
505     notfirst=False
506     for i,v in self:
507     if notfirst: out=out+","
508     notfirst=True
509     if isinstance(v,ParameterSet):
510     out="%s\"%s\" : %s"%(out,i,v.showParameters())
511     else:
512     out="%s\"%s\" : %s"%(out,i,v)
513     return out+"}"
514    
515     def __delattr__(self,name):
516 jgs 149 """
517     Removes the attribute name.
518     """
519 jgs 123 LinkableObject.__delattr__(self,name)
520     try:
521     self.releaseParameter(name)
522     except:
523     pass
524 jgs 121
525 gross 918 def toDom(self, esysxml, node):
526 jgs 149 """
527 gross 918 C{toDom} method of Model class
528 jgs 149 """
529 gross 918 pset = esysxml.createElement('ParameterSet')
530     pset.setAttribute('type', self.__class__.__name__)
531     pset.setAttribute('module', self.__class__.__module__)
532     esysxml.registerLinkableObject(self, pset)
533     self._parametersToDom(esysxml, pset)
534 jgs 123 node.appendChild(pset)
535 jgs 121
536 gross 918 def _parametersToDom(self, esysxml, node):
537 jgs 123 for name,value in self:
538 gross 918 # convert list to numarray when possible:
539     if isinstance (value, list):
540     elem_type=-1
541     for i in value:
542     if isinstance(i, bool):
543     elem_type = max(elem_type,0)
544 gross 964 elif isinstance(i, int):
545 gross 918 elem_type = max(elem_type,1)
546 gross 964 elif isinstance(i, float):
547 gross 918 elem_type = max(elem_type,2)
548     if elem_type == 0: value = numarray.array(value,numarray.Bool)
549     if elem_type == 1: value = numarray.array(value,numarray.Int)
550     if elem_type == 2: value = numarray.array(value,numarray.Float)
551    
552     param = esysxml.createElement('Parameter')
553 jgs 123 param.setAttribute('type', value.__class__.__name__)
554 jgs 121
555 gross 918 param.appendChild(esysxml.createDataNode('Name', name))
556 jgs 121
557 gross 918 val = esysxml.createElement('Value')
558 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
559 gross 918 value.toDom(esysxml, val)
560 jgs 123 param.appendChild(val)
561 elspeth 871 elif isinstance(value, numarray.NumArray):
562     shape = value.getshape()
563     if isinstance(shape, tuple):
564     size = reduce(operator.mul, shape)
565     shape = ' '.join(map(str, shape))
566     else:
567     size = shape
568     shape = str(shape)
569    
570     arraytype = value.type()
571 gross 918 if isinstance(arraytype, numarray.BooleanType):
572     arraytype_str="Bool"
573     elif isinstance(arraytype, numarray.IntegralType):
574     arraytype_str="Int"
575     elif isinstance(arraytype, numarray.FloatingType):
576     arraytype_str="Float"
577     elif isinstance(arraytype, numarray.ComplexType):
578     arraytype_str="Complex"
579     else:
580     arraytype_str=str(arraytype)
581     numarrayElement = esysxml.createElement('NumArray')
582     numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
583     numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
584     numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
585 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
586 elspeth 874 val.appendChild(numarrayElement)
587 elspeth 871 param.appendChild(val)
588 gross 918 elif isinstance(value, list):
589     param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
590     elif isinstance(value, (str, bool, int, float, type(None))):
591     param.appendChild(esysxml.createDataNode('Value', str(value)))
592 gross 926 elif isinstance(value, dict):
593     dic = esysxml.createElement('dictionary')
594     if len(value.keys())>0:
595     dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
596     dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
597     for k,v in value.items():
598     i=esysxml.createElement('item')
599     i.appendChild(esysxml.createDataNode('key', k))
600     i.appendChild(esysxml.createDataNode('value', v))
601     dic.appendChild(i)
602     param.appendChild(dic)
603 jgs 123 else:
604 gross 918 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
605 jgs 123
606     node.appendChild(param)
607    
608 gross 918 def fromDom(cls, esysxml, node):
609 jgs 123 # Define a host of helper functions to assist us.
610     def _children(node):
611     """
612 jgs 149 Remove the empty nodes from the children of this node.
613 jgs 123 """
614 elspeth 871 ret = []
615     for x in node.childNodes:
616     if isinstance(x, minidom.Text):
617     if x.nodeValue.strip():
618     ret.append(x)
619     else:
620     ret.append(x)
621     return ret
622 jgs 123
623 gross 918 def _floatfromValue(esysxml, node):
624     return float(node.nodeValue.strip())
625 jgs 123
626 gross 918 def _stringfromValue(esysxml, node):
627     return str(node.nodeValue.strip())
628 jgs 126
629 gross 918 def _intfromValue(esysxml, node):
630     return int(node.nodeValue.strip())
631 jgs 126
632 gross 918 def _boolfromValue(esysxml, node):
633     return _boolfromstring(node.nodeValue.strip())
634 elspeth 266
635 gross 918 def _nonefromValue(esysxml, node):
636 elspeth 266 return None
637 elspeth 871
638 gross 918 def _numarrayfromValue(esysxml, node):
639     for node in _children(node):
640 elspeth 871 if node.tagName == 'ArrayType':
641     arraytype = node.firstChild.nodeValue.strip()
642     if node.tagName == 'Shape':
643     shape = node.firstChild.nodeValue.strip()
644     shape = [int(x) for x in shape.split()]
645     if node.tagName == 'Data':
646     data = node.firstChild.nodeValue.strip()
647     data = [float(x) for x in data.split()]
648     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
649     shape)
650 elspeth 874
651 gross 918 def _listfromValue(esysxml, node):
652     return [x for x in node.nodeValue.split()]
653 elspeth 874
654     def _boolfromstring(s):
655     if s == 'True':
656     return True
657     else:
658     return False
659 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
660     ptypemap = {"Simulation": Simulation.fromDom,
661     "Model":Model.fromDom,
662     "ParameterSet":ParameterSet.fromDom,
663     "Link":Link.fromDom,
664 elspeth 875 "DataSource":DataSource.fromDom,
665 jgs 123 "float":_floatfromValue,
666 jgs 126 "int":_intfromValue,
667 jgs 123 "str":_stringfromValue,
668 elspeth 269 "bool":_boolfromValue,
669 elspeth 874 "list":_listfromValue,
670     "NumArray":_numarrayfromValue,
671 elspeth 871 "NoneType":_nonefromValue,
672 jgs 123 }
673    
674     parameters = {}
675 gross 918 for n in _children(node):
676     ptype = n.getAttribute("type")
677     if not ptypemap.has_key(ptype):
678     raise KeyError("cannot handle parameter type %s."%ptype)
679 jgs 123
680     pname = pvalue = None
681 gross 918 for childnode in _children(n):
682 jgs 123 if childnode.tagName == "Name":
683     pname = childnode.firstChild.nodeValue.strip()
684    
685     if childnode.tagName == "Value":
686     nodes = _children(childnode)
687 gross 918 pvalue = ptypemap[ptype](esysxml, nodes[0])
688 jgs 123
689     parameters[pname] = pvalue
690    
691     # Create the instance of ParameterSet
692 gross 918 o = cls(debug=esysxml.debug)
693 jgs 123 o.declareParameters(parameters)
694 gross 918 esysxml.registerLinkableObject(o, node)
695 jgs 123 return o
696    
697     fromDom = classmethod(fromDom)
698 gross 918
699 jgs 123 def writeXML(self,ostream=stdout):
700 jgs 149 """
701     Writes the object as an XML object into an output stream.
702     """
703 gross 918 esysxml=ESySXMLCreator()
704     self.toDom(esysxml, esysxml.getRoot())
705     ostream.write(esysxml.toprettyxml())
706    
707 jgs 147 class Model(ParameterSet):
708     """
709 jgs 149 A Model object represents a processess marching over time until a
710     finalizing condition is fullfilled. At each time step an iterative
711     process can be performed and the time step size can be controlled. A
712     Model has the following work flow::
713 gross 906
714 jgs 147 doInitialization()
715 gross 906 while not terminateInitialIteration(): doInitializationiStep()
716     doInitialPostprocessing()
717 jgs 147 while not finalize():
718     dt=getSafeTimeStepSize(dt)
719     doStepPreprocessing(dt)
720     while not terminateIteration(): doStep(dt)
721     doStepPostprocessing(dt)
722     doFinalization()
723 jgs 121
724 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
725     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
726     C{doFinalization} are methods of the particular instance of a Model. The
727     default implementations of these methods have to be overwritten by the
728     subclass implementing a Model.
729 jgs 147 """
730 jgs 121
731 jgs 147 UNDEF_DT=1.e300
732 jgs 121
733 gross 918 def __init__(self,parameters=[],**kwargs):
734 jgs 149 """
735     Creates a model.
736 jgs 121
737 jgs 149 Just calls the parent constructor.
738 jgs 147 """
739 gross 918 ParameterSet.__init__(self, parameters=parameters,**kwargs)
740 jgs 121
741 jgs 147 def __str__(self):
742 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
743 jgs 121
744    
745 jgs 147 def doInitialization(self):
746 jgs 149 """
747     Initializes the time stepping scheme.
748    
749     This function may be overwritten.
750     """
751 jgs 147 pass
752 gross 906 def doInitialStep(self):
753     """
754     performs an iteration step in the initialization phase
755    
756     This function may be overwritten.
757     """
758     pass
759    
760     def terminateInitialIteration(self):
761     """
762     Returns True if iteration at the inital phase is terminated.
763     """
764     return True
765    
766     def doInitialPostprocessing(self):
767     """
768     finalises the initialization iteration process
769    
770     This function may be overwritten.
771     """
772     pass
773 jgs 147
774     def getSafeTimeStepSize(self,dt):
775 jgs 149 """
776     Returns a time step size which can safely be used.
777    
778     C{dt} gives the previously used step size.
779    
780     This function may be overwritten.
781     """
782 jgs 147 return self.UNDEF_DT
783    
784     def finalize(self):
785 jgs 149 """
786     Returns False if the time stepping is finalized.
787    
788     This function may be overwritten.
789     """
790 jgs 147 return False
791 jgs 123
792 jgs 147 def doFinalization(self):
793 jgs 149 """
794     Finalizes the time stepping.
795    
796     This function may be overwritten.
797     """
798 jgs 147 pass
799    
800     def doStepPreprocessing(self,dt):
801 jgs 149 """
802     Sets up a time step of step size dt.
803    
804     This function may be overwritten.
805     """
806 jgs 147 pass
807    
808     def doStep(self,dt):
809 jgs 149 """
810     Executes an iteration step at a time step.
811    
812     C{dt} is the currently used time step size.
813    
814     This function may be overwritten.
815     """
816 jgs 147 pass
817    
818     def terminateIteration(self):
819 jgs 149 """
820     Returns True if iteration on a time step is terminated.
821     """
822 jgs 147 return True
823 gross 906
824 jgs 147
825     def doStepPostprocessing(self,dt):
826 jgs 149 """
827 gross 906 finalises the time step.
828 jgs 149
829     dt is the currently used time step size.
830    
831     This function may be overwritten.
832     """
833 jgs 147 pass
834 gross 918
835     def toDom(self, esysxml, node):
836     """
837     C{toDom} method of Model class
838     """
839     pset = esysxml.createElement('Model')
840     pset.setAttribute('type', self.__class__.__name__)
841     pset.setAttribute('module', self.__class__.__module__)
842     esysxml.registerLinkableObject(self, pset)
843     node.appendChild(pset)
844     self._parametersToDom(esysxml, pset)
845 jgs 147
846     class Simulation(Model):
847     """
848 jgs 149 A Simulation object is special Model which runs a sequence of Models.
849 jgs 121
850 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
851     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
852     C{doFinalization} are executing the corresponding methods of the models in
853     the simulation.
854 jgs 147 """
855    
856     FAILED_TIME_STEPS_MAX=20
857     MAX_ITER_STEPS=50
858 gross 836 MAX_CHANGE_OF_DT=2.
859 jgs 147
860     def __init__(self, models=[], **kwargs):
861 jgs 149 """
862     Initiates a simulation from a list of models.
863     """
864 gross 918 super(Simulation, self).__init__(**kwargs)
865 gross 938 for m in models:
866     if not isinstance(m, Model):
867     raise TypeError("%s is not a subclass of Model."%m)
868 jgs 147 self.__models=[]
869     for i in range(len(models)):
870     self[i] = models[i]
871 jgs 149
872 jgs 121
873 jgs 147 def __repr__(self):
874     """
875 jgs 149 Returns a string representation of the Simulation.
876 jgs 147 """
877     return "<Simulation %r>" % self.__models
878 jgs 121
879 jgs 147 def __str__(self):
880     """
881 jgs 149 Returning Simulation as a string.
882 jgs 147 """
883     return "<Simulation %d>"%id(self)
884    
885     def iterModels(self):
886 jgs 149 """
887     Returns an iterator over the models.
888     """
889 jgs 147 return self.__models
890    
891     def __getitem__(self,i):
892 jgs 149 """
893     Returns the i-th model.
894     """
895 jgs 147 return self.__models[i]
896    
897     def __setitem__(self,i,value):
898 jgs 149 """
899     Sets the i-th model.
900     """
901 jgs 147 if not isinstance(value,Model):
902 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
903 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
904     self.__models.append(None)
905 jgs 147 self.__models[i]=value
906    
907     def __len__(self):
908 jgs 149 """
909     Returns the number of models.
910     """
911 jgs 147 return len(self.__models)
912 jgs 121
913 gross 938 def getAllModels(self):
914     """
915     returns a list of all models used in the Simulation including subsimulations
916     """
917     out=[]
918     for m in self.iterModels():
919     if isinstance(m, Simulation):
920     out+=m.getAllModels()
921     else:
922     out.append(m)
923     return list(set(out))
924    
925 gross 950 def checkModels(self, models, hash):
926 gross 938 """
927     returns a list of (model,parameter, target model ) if the the parameter of model
928     is linking to the target_model which is not in list of models.
929     """
930 gross 952 out=self.checkLinkTargets(models, hash + [self])
931 gross 938 for m in self.iterModels():
932     if isinstance(m, Simulation):
933 gross 952 out|=m.checkModels(models, hash)
934 gross 938 else:
935 gross 952 out|=m.checkLinkTargets(models, hash + [self])
936     return set( [ (str(self)+"."+f[0],f[1]) for f in out ] )
937 gross 938
938 jgs 147
939     def getSafeTimeStepSize(self,dt):
940 jgs 149 """
941     Returns a time step size which can safely be used by all models.
942    
943     This is the minimum over the time step sizes of all models.
944     """
945 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
946     return out
947    
948     def doInitialization(self):
949 jgs 149 """
950     Initializes all models.
951     """
952 jgs 147 self.n=0
953     self.tn=0.
954     for o in self.iterModels():
955 gross 906 o.doInitialization()
956     def doInitialStep(self):
957     """
958     performs an iteration step in the initialization step for all models
959     """
960 gross 908 iter=0
961     while not self.terminateInitialIteration():
962     if iter==0: self.trace("iteration for initialization starts")
963     iter+=1
964     self.trace("iteration step %d"%(iter))
965     for o in self.iterModels():
966     o.doInitialStep()
967     if iter>self.MAX_ITER_STEPS:
968     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
969     self.trace("Initialization finalized after %s iteration steps."%iter)
970 gross 906
971     def doInitialPostprocessing(self):
972     """
973     finalises the initialization iteration process for all models.
974     """
975     for o in self.iterModels():
976     o.doInitialPostprocessing()
977 jgs 147 def finalize(self):
978 jgs 149 """
979     Returns True if any of the models is to be finalized.
980     """
981 jgs 147 return any([o.finalize() for o in self.iterModels()])
982 jgs 123
983 jgs 147 def doFinalization(self):
984 jgs 149 """
985 gross 906 finalises the time stepping for all models.
986 jgs 149 """
987 jgs 147 for i in self.iterModels(): i.doFinalization()
988     self.trace("end of time integation.")
989    
990     def doStepPreprocessing(self,dt):
991 jgs 149 """
992     Initializes the time step for all models.
993     """
994 jgs 147 for o in self.iterModels():
995     o.doStepPreprocessing(dt)
996    
997     def terminateIteration(self):
998 jgs 149 """
999     Returns True if all iterations for all models are terminated.
1000     """
1001 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
1002     return out
1003 gross 906
1004     def terminateInitialIteration(self):
1005     """
1006     Returns True if all initial iterations for all models are terminated.
1007     """
1008     out=all([o.terminateInitialIteration() for o in self.iterModels()])
1009     return out
1010 jgs 123
1011 jgs 147 def doStepPostprocessing(self,dt):
1012 jgs 149 """
1013 gross 906 finalises the iteration process for all models.
1014 jgs 149 """
1015 jgs 147 for o in self.iterModels():
1016     o.doStepPostprocessing(dt)
1017     self.n+=1
1018     self.tn+=dt
1019    
1020     def doStep(self,dt):
1021     """
1022 jgs 149 Executes the iteration step at a time step for all model::
1023 jgs 147
1024 jgs 149 self.doStepPreprocessing(dt)
1025     while not self.terminateIteration():
1026     for all models:
1027     self.doStep(dt)
1028     self.doStepPostprocessing(dt)
1029 jgs 147 """
1030     self.iter=0
1031     while not self.terminateIteration():
1032     if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
1033     self.iter+=1
1034     self.trace("iteration step %d"%(self.iter))
1035     for o in self.iterModels():
1036     o.doStep(dt)
1037     if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
1038 jgs 121
1039 jgs 147 def run(self,check_point=None):
1040     """
1041 jgs 149 Run the simulation by performing essentially::
1042 jgs 123
1043 jgs 149 self.doInitialization()
1044 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
1045     self.doInitialPostprocessing()
1046 jgs 149 while not self.finalize():
1047     dt=self.getSafeTimeStepSize()
1048     self.doStep(dt)
1049     if n%check_point==0:
1050     self.writeXML()
1051     self.doFinalization()
1052 jgs 121
1053 jgs 149 If one of the models in throws a C{FailedTimeStepError} exception a
1054     new time step size is computed through getSafeTimeStepSize() and the
1055     time step is repeated.
1056 jgs 121
1057 jgs 149 If one of the models in throws a C{IterationDivergenceError}
1058     exception the time step size is halved and the time step is repeated.
1059 jgs 121
1060 jgs 149 In both cases the time integration is given up after
1061     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1062 jgs 147 """
1063 gross 938 # check the completness of the models:
1064     # first a list of all the models involved in the simulation including subsimulations:
1065     #
1066 gross 950 missing=self.checkModels(self.getAllModels(), [])
1067 gross 938 if len(missing)>0:
1068     msg=""
1069     for l in missing:
1070 gross 952 msg+="\n\t"+str(l[1])+" at "+l[0]
1071 gross 938 raise MissingLink("link targets missing in the Simulation: %s"%msg)
1072     #==============================
1073 gross 906 self.doInitialization()
1074 gross 908 self.doInitialStep()
1075 gross 906 self.doInitialPostprocessing()
1076 jgs 147 dt=self.UNDEF_DT
1077     while not self.finalize():
1078     step_fail_counter=0
1079     iteration_fail_counter=0
1080 gross 836 if self.n==0:
1081     dt_new=self.getSafeTimeStepSize(dt)
1082     else:
1083 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1084 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1085     end_of_step=False
1086     while not end_of_step:
1087     end_of_step=True
1088     if not dt_new>0:
1089 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1090 jgs 147 try:
1091     self.doStepPreprocessing(dt_new)
1092     self.doStep(dt_new)
1093     self.doStepPostprocessing(dt_new)
1094     except IterationDivergenceError:
1095     dt_new*=0.5
1096     end_of_step=False
1097     iteration_fail_counter+=1
1098     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1099 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1100     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1101 jgs 147 except FailedTimeStepError:
1102     dt_new=self.getSafeTimeStepSize(dt)
1103     end_of_step=False
1104     step_fail_counter+=1
1105 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1106 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1107 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1108 jgs 147 dt=dt_new
1109     if not check_point==None:
1110     if n%check_point==0:
1111     self.trace("check point is created.")
1112     self.writeXML()
1113     self.doFinalization()
1114 jgs 121
1115 gross 918
1116     def toDom(self, esysxml, node):
1117     """
1118     C{toDom} method of Simulation class.
1119     """
1120     simulation = esysxml.createElement('Simulation')
1121     esysxml.registerLinkableObject(self, simulation)
1122     for rank, sim in enumerate(self.iterModels()):
1123     component = esysxml.createElement('Component')
1124     component.setAttribute('rank', str(rank))
1125     sim.toDom(esysxml, component)
1126     simulation.appendChild(component)
1127     node.appendChild(simulation)
1128    
1129    
1130     def fromDom(cls, esysxml, node):
1131 jgs 147 sims = []
1132 gross 918 for n in node.childNodes:
1133     if isinstance(n, minidom.Text):
1134 jgs 147 continue
1135 gross 918 sims.append(esysxml.getComponent(n))
1136     sims.sort(cmp=_comp)
1137     sim=cls([s[1] for s in sims], debug=esysxml.debug)
1138     esysxml.registerLinkableObject(sim, node)
1139     return sim
1140 jgs 121
1141 jgs 147 fromDom = classmethod(fromDom)
1142 jgs 121
1143 gross 918 def _comp(a,b):
1144     if a[0]<a[1]:
1145     return 1
1146     elif a[0]>a[1]:
1147     return -1
1148     else:
1149     return 0
1150 jgs 121
1151 jgs 147 class IterationDivergenceError(Exception):
1152     """
1153 jgs 149 Exception which is thrown if there is no convergence of the iteration
1154     process at a time step.
1155 jgs 121
1156 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1157 jgs 147 """
1158     pass
1159 jgs 121
1160 jgs 147 class FailedTimeStepError(Exception):
1161 jgs 149 """
1162     Exception which is thrown if the time step fails because of a step
1163     size that have been choosen to be too large.
1164     """
1165 jgs 147 pass
1166 jgs 121
1167 jgs 147 class SimulationBreakDownError(Exception):
1168 jgs 149 """
1169     Exception which is thrown if the simulation does not manage to
1170     progress in time.
1171     """
1172 jgs 147 pass
1173 jgs 121
1174 jgs 147 class NonPositiveStepSizeError(Exception):
1175 jgs 149 """
1176     Exception which is thrown if the step size is not positive.
1177     """
1178 jgs 147 pass
1179 jgs 149
1180 gross 938 class MissingLink(Exception):
1181     """
1182     Exception thrown when a link is missing
1183     """
1184     pass
1185    
1186 elspeth 875 class DataSource(object):
1187     """
1188     Class for handling data sources, including local and remote files. This class is under development.
1189     """
1190    
1191 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1192 elspeth 875 self.uri = uri
1193     self.fileformat = fileformat
1194    
1195 gross 918 def toDom(self, esysxml, node):
1196 elspeth 875 """
1197     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1198 gross 918 current XML esysxml.
1199 elspeth 875 """
1200 gross 918 ds = esysxml.createElement('DataSource')
1201     ds.appendChild(esysxml.createDataNode('URI', self.uri))
1202     ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1203 elspeth 875 node.appendChild(ds)
1204    
1205 gross 918 def fromDom(cls, esysxml, node):
1206 gross 920 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1207     fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1208 elspeth 875 ds = cls(uri, fileformat)
1209     return ds
1210    
1211 gross 885 def getLocalFileName(self):
1212     return self.uri
1213    
1214 elspeth 875 fromDom = classmethod(fromDom)
1215    
1216 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