/[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 920 - (hide annotations)
Thu Jan 4 00:00:40 2007 UTC (12 years, 11 months ago) by gross
File MIME type: text/x-python
File size: 37432 byte(s)
small fix on DataSource.fromDom
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 jgs 123 else:
574 gross 918 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
575 jgs 123
576     node.appendChild(param)
577    
578 gross 918 def fromDom(cls, esysxml, node):
579 jgs 123 # Define a host of helper functions to assist us.
580     def _children(node):
581     """
582 jgs 149 Remove the empty nodes from the children of this node.
583 jgs 123 """
584 elspeth 871 ret = []
585     for x in node.childNodes:
586     if isinstance(x, minidom.Text):
587     if x.nodeValue.strip():
588     ret.append(x)
589     else:
590     ret.append(x)
591     return ret
592 jgs 123
593 gross 918 def _floatfromValue(esysxml, node):
594     return float(node.nodeValue.strip())
595 jgs 123
596 gross 918 def _stringfromValue(esysxml, node):
597     return str(node.nodeValue.strip())
598 jgs 126
599 gross 918 def _intfromValue(esysxml, node):
600     return int(node.nodeValue.strip())
601 jgs 126
602 gross 918 def _boolfromValue(esysxml, node):
603     return _boolfromstring(node.nodeValue.strip())
604 elspeth 266
605 gross 918 def _nonefromValue(esysxml, node):
606 elspeth 266 return None
607 elspeth 871
608 gross 918 def _numarrayfromValue(esysxml, node):
609     for node in _children(node):
610 elspeth 871 if node.tagName == 'ArrayType':
611     arraytype = node.firstChild.nodeValue.strip()
612     if node.tagName == 'Shape':
613     shape = node.firstChild.nodeValue.strip()
614     shape = [int(x) for x in shape.split()]
615     if node.tagName == 'Data':
616     data = node.firstChild.nodeValue.strip()
617     data = [float(x) for x in data.split()]
618     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
619     shape)
620 elspeth 874
621 gross 918 def _listfromValue(esysxml, node):
622     return [x for x in node.nodeValue.split()]
623 elspeth 874
624     def _boolfromstring(s):
625     if s == 'True':
626     return True
627     else:
628     return False
629 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
630     ptypemap = {"Simulation": Simulation.fromDom,
631     "Model":Model.fromDom,
632     "ParameterSet":ParameterSet.fromDom,
633     "Link":Link.fromDom,
634 elspeth 875 "DataSource":DataSource.fromDom,
635 jgs 123 "float":_floatfromValue,
636 jgs 126 "int":_intfromValue,
637 jgs 123 "str":_stringfromValue,
638 elspeth 269 "bool":_boolfromValue,
639 elspeth 874 "list":_listfromValue,
640     "NumArray":_numarrayfromValue,
641 elspeth 871 "NoneType":_nonefromValue,
642 jgs 123 }
643    
644     parameters = {}
645 gross 918 for n in _children(node):
646     ptype = n.getAttribute("type")
647     if not ptypemap.has_key(ptype):
648     raise KeyError("cannot handle parameter type %s."%ptype)
649 jgs 123
650     pname = pvalue = None
651 gross 918 for childnode in _children(n):
652 jgs 123 if childnode.tagName == "Name":
653     pname = childnode.firstChild.nodeValue.strip()
654    
655     if childnode.tagName == "Value":
656     nodes = _children(childnode)
657 gross 918 pvalue = ptypemap[ptype](esysxml, nodes[0])
658 jgs 123
659     parameters[pname] = pvalue
660    
661     # Create the instance of ParameterSet
662 gross 918 o = cls(debug=esysxml.debug)
663 jgs 123 o.declareParameters(parameters)
664 gross 918 esysxml.registerLinkableObject(o, node)
665 jgs 123 return o
666    
667     fromDom = classmethod(fromDom)
668 gross 918
669 jgs 123 def writeXML(self,ostream=stdout):
670 jgs 149 """
671     Writes the object as an XML object into an output stream.
672     """
673 gross 918 esysxml=ESySXMLCreator()
674     self.toDom(esysxml, esysxml.getRoot())
675     ostream.write(esysxml.toprettyxml())
676    
677 jgs 147 class Model(ParameterSet):
678     """
679 jgs 149 A Model object represents a processess marching over time until a
680     finalizing condition is fullfilled. At each time step an iterative
681     process can be performed and the time step size can be controlled. A
682     Model has the following work flow::
683 gross 906
684 jgs 147 doInitialization()
685 gross 906 while not terminateInitialIteration(): doInitializationiStep()
686     doInitialPostprocessing()
687 jgs 147 while not finalize():
688     dt=getSafeTimeStepSize(dt)
689     doStepPreprocessing(dt)
690     while not terminateIteration(): doStep(dt)
691     doStepPostprocessing(dt)
692     doFinalization()
693 jgs 121
694 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
695     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
696     C{doFinalization} are methods of the particular instance of a Model. The
697     default implementations of these methods have to be overwritten by the
698     subclass implementing a Model.
699 jgs 147 """
700 jgs 121
701 jgs 147 UNDEF_DT=1.e300
702 jgs 121
703 gross 918 def __init__(self,parameters=[],**kwargs):
704 jgs 149 """
705     Creates a model.
706 jgs 121
707 jgs 149 Just calls the parent constructor.
708 jgs 147 """
709 gross 918 ParameterSet.__init__(self, parameters=parameters,**kwargs)
710 jgs 121
711 jgs 147 def __str__(self):
712 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
713 jgs 121
714    
715 jgs 147 def doInitialization(self):
716 jgs 149 """
717     Initializes the time stepping scheme.
718    
719     This function may be overwritten.
720     """
721 jgs 147 pass
722 gross 906 def doInitialStep(self):
723     """
724     performs an iteration step in the initialization phase
725    
726     This function may be overwritten.
727     """
728     pass
729    
730     def terminateInitialIteration(self):
731     """
732     Returns True if iteration at the inital phase is terminated.
733     """
734     return True
735    
736     def doInitialPostprocessing(self):
737     """
738     finalises the initialization iteration process
739    
740     This function may be overwritten.
741     """
742     pass
743 jgs 147
744     def getSafeTimeStepSize(self,dt):
745 jgs 149 """
746     Returns a time step size which can safely be used.
747    
748     C{dt} gives the previously used step size.
749    
750     This function may be overwritten.
751     """
752 jgs 147 return self.UNDEF_DT
753    
754     def finalize(self):
755 jgs 149 """
756     Returns False if the time stepping is finalized.
757    
758     This function may be overwritten.
759     """
760 jgs 147 return False
761 jgs 123
762 jgs 147 def doFinalization(self):
763 jgs 149 """
764     Finalizes the time stepping.
765    
766     This function may be overwritten.
767     """
768 jgs 147 pass
769    
770     def doStepPreprocessing(self,dt):
771 jgs 149 """
772     Sets up a time step of step size dt.
773    
774     This function may be overwritten.
775     """
776 jgs 147 pass
777    
778     def doStep(self,dt):
779 jgs 149 """
780     Executes an iteration step at a time step.
781    
782     C{dt} is the currently used time step size.
783    
784     This function may be overwritten.
785     """
786 jgs 147 pass
787    
788     def terminateIteration(self):
789 jgs 149 """
790     Returns True if iteration on a time step is terminated.
791     """
792 jgs 147 return True
793 gross 906
794 jgs 147
795     def doStepPostprocessing(self,dt):
796 jgs 149 """
797 gross 906 finalises the time step.
798 jgs 149
799     dt is the currently used time step size.
800    
801     This function may be overwritten.
802     """
803 jgs 147 pass
804 gross 918
805     def toDom(self, esysxml, node):
806     """
807     C{toDom} method of Model class
808     """
809     pset = esysxml.createElement('Model')
810     pset.setAttribute('type', self.__class__.__name__)
811     pset.setAttribute('module', self.__class__.__module__)
812     esysxml.registerLinkableObject(self, pset)
813     node.appendChild(pset)
814     self._parametersToDom(esysxml, pset)
815 jgs 147
816     class Simulation(Model):
817     """
818 jgs 149 A Simulation object is special Model which runs a sequence of Models.
819 jgs 121
820 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
821     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
822     C{doFinalization} are executing the corresponding methods of the models in
823     the simulation.
824 jgs 147 """
825    
826     FAILED_TIME_STEPS_MAX=20
827     MAX_ITER_STEPS=50
828 gross 836 MAX_CHANGE_OF_DT=2.
829 jgs 147
830     def __init__(self, models=[], **kwargs):
831 jgs 149 """
832     Initiates a simulation from a list of models.
833     """
834 gross 918 super(Simulation, self).__init__(**kwargs)
835 jgs 147 self.__models=[]
836 jgs 149
837 jgs 147 for i in range(len(models)):
838     self[i] = models[i]
839 jgs 149
840 jgs 121
841 jgs 147 def __repr__(self):
842     """
843 jgs 149 Returns a string representation of the Simulation.
844 jgs 147 """
845     return "<Simulation %r>" % self.__models
846 jgs 121
847 jgs 147 def __str__(self):
848     """
849 jgs 149 Returning Simulation as a string.
850 jgs 147 """
851     return "<Simulation %d>"%id(self)
852    
853     def iterModels(self):
854 jgs 149 """
855     Returns an iterator over the models.
856     """
857 jgs 147 return self.__models
858    
859     def __getitem__(self,i):
860 jgs 149 """
861     Returns the i-th model.
862     """
863 jgs 147 return self.__models[i]
864    
865     def __setitem__(self,i,value):
866 jgs 149 """
867     Sets the i-th model.
868     """
869 jgs 147 if not isinstance(value,Model):
870 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
871 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
872     self.__models.append(None)
873 jgs 147 self.__models[i]=value
874    
875     def __len__(self):
876 jgs 149 """
877     Returns the number of models.
878     """
879 jgs 147 return len(self.__models)
880 jgs 121
881 jgs 147
882     def getSafeTimeStepSize(self,dt):
883 jgs 149 """
884     Returns a time step size which can safely be used by all models.
885    
886     This is the minimum over the time step sizes of all models.
887     """
888 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
889     return out
890    
891     def doInitialization(self):
892 jgs 149 """
893     Initializes all models.
894     """
895 jgs 147 self.n=0
896     self.tn=0.
897     for o in self.iterModels():
898 gross 906 o.doInitialization()
899     def doInitialStep(self):
900     """
901     performs an iteration step in the initialization step for all models
902     """
903 gross 908 iter=0
904     while not self.terminateInitialIteration():
905     if iter==0: self.trace("iteration for initialization starts")
906     iter+=1
907     self.trace("iteration step %d"%(iter))
908     for o in self.iterModels():
909     o.doInitialStep()
910     if iter>self.MAX_ITER_STEPS:
911     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
912     self.trace("Initialization finalized after %s iteration steps."%iter)
913 gross 906
914     def doInitialPostprocessing(self):
915     """
916     finalises the initialization iteration process for all models.
917     """
918     for o in self.iterModels():
919     o.doInitialPostprocessing()
920 jgs 147 def finalize(self):
921 jgs 149 """
922     Returns True if any of the models is to be finalized.
923     """
924 jgs 147 return any([o.finalize() for o in self.iterModels()])
925 jgs 123
926 jgs 147 def doFinalization(self):
927 jgs 149 """
928 gross 906 finalises the time stepping for all models.
929 jgs 149 """
930 jgs 147 for i in self.iterModels(): i.doFinalization()
931     self.trace("end of time integation.")
932    
933     def doStepPreprocessing(self,dt):
934 jgs 149 """
935     Initializes the time step for all models.
936     """
937 jgs 147 for o in self.iterModels():
938     o.doStepPreprocessing(dt)
939    
940     def terminateIteration(self):
941 jgs 149 """
942     Returns True if all iterations for all models are terminated.
943     """
944 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
945     return out
946 gross 906
947     def terminateInitialIteration(self):
948     """
949     Returns True if all initial iterations for all models are terminated.
950     """
951     out=all([o.terminateInitialIteration() for o in self.iterModels()])
952     return out
953 jgs 123
954 jgs 147 def doStepPostprocessing(self,dt):
955 jgs 149 """
956 gross 906 finalises the iteration process for all models.
957 jgs 149 """
958 jgs 147 for o in self.iterModels():
959     o.doStepPostprocessing(dt)
960     self.n+=1
961     self.tn+=dt
962    
963     def doStep(self,dt):
964     """
965 jgs 149 Executes the iteration step at a time step for all model::
966 jgs 147
967 jgs 149 self.doStepPreprocessing(dt)
968     while not self.terminateIteration():
969     for all models:
970     self.doStep(dt)
971     self.doStepPostprocessing(dt)
972 jgs 147 """
973     self.iter=0
974     while not self.terminateIteration():
975     if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
976     self.iter+=1
977     self.trace("iteration step %d"%(self.iter))
978     for o in self.iterModels():
979     o.doStep(dt)
980     if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
981 jgs 121
982 jgs 147 def run(self,check_point=None):
983     """
984 jgs 149 Run the simulation by performing essentially::
985 jgs 123
986 jgs 149 self.doInitialization()
987 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
988     self.doInitialPostprocessing()
989 jgs 149 while not self.finalize():
990     dt=self.getSafeTimeStepSize()
991     self.doStep(dt)
992     if n%check_point==0:
993     self.writeXML()
994     self.doFinalization()
995 jgs 121
996 jgs 149 If one of the models in throws a C{FailedTimeStepError} exception a
997     new time step size is computed through getSafeTimeStepSize() and the
998     time step is repeated.
999 jgs 121
1000 jgs 149 If one of the models in throws a C{IterationDivergenceError}
1001     exception the time step size is halved and the time step is repeated.
1002 jgs 121
1003 jgs 149 In both cases the time integration is given up after
1004     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1005 jgs 147 """
1006 gross 906 self.doInitialization()
1007 gross 908 self.doInitialStep()
1008 gross 906 self.doInitialPostprocessing()
1009 jgs 147 dt=self.UNDEF_DT
1010     while not self.finalize():
1011     step_fail_counter=0
1012     iteration_fail_counter=0
1013 gross 836 if self.n==0:
1014     dt_new=self.getSafeTimeStepSize(dt)
1015     else:
1016 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1017 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1018     end_of_step=False
1019     while not end_of_step:
1020     end_of_step=True
1021     if not dt_new>0:
1022 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1023 jgs 147 try:
1024     self.doStepPreprocessing(dt_new)
1025     self.doStep(dt_new)
1026     self.doStepPostprocessing(dt_new)
1027     except IterationDivergenceError:
1028     dt_new*=0.5
1029     end_of_step=False
1030     iteration_fail_counter+=1
1031     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1032 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1033     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1034 jgs 147 except FailedTimeStepError:
1035     dt_new=self.getSafeTimeStepSize(dt)
1036     end_of_step=False
1037     step_fail_counter+=1
1038 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1039 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1040 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1041 jgs 147 dt=dt_new
1042     if not check_point==None:
1043     if n%check_point==0:
1044     self.trace("check point is created.")
1045     self.writeXML()
1046     self.doFinalization()
1047 jgs 121
1048 gross 918
1049     def toDom(self, esysxml, node):
1050     """
1051     C{toDom} method of Simulation class.
1052     """
1053     simulation = esysxml.createElement('Simulation')
1054     esysxml.registerLinkableObject(self, simulation)
1055     for rank, sim in enumerate(self.iterModels()):
1056     component = esysxml.createElement('Component')
1057     component.setAttribute('rank', str(rank))
1058     sim.toDom(esysxml, component)
1059     simulation.appendChild(component)
1060     node.appendChild(simulation)
1061    
1062    
1063     def fromDom(cls, esysxml, node):
1064 jgs 147 sims = []
1065 gross 918 for n in node.childNodes:
1066     if isinstance(n, minidom.Text):
1067 jgs 147 continue
1068 gross 918 sims.append(esysxml.getComponent(n))
1069     sims.sort(cmp=_comp)
1070     sim=cls([s[1] for s in sims], debug=esysxml.debug)
1071     esysxml.registerLinkableObject(sim, node)
1072     return sim
1073 jgs 121
1074 jgs 147 fromDom = classmethod(fromDom)
1075 jgs 121
1076 gross 918 def _comp(a,b):
1077     if a[0]<a[1]:
1078     return 1
1079     elif a[0]>a[1]:
1080     return -1
1081     else:
1082     return 0
1083 jgs 121
1084 jgs 147 class IterationDivergenceError(Exception):
1085     """
1086 jgs 149 Exception which is thrown if there is no convergence of the iteration
1087     process at a time step.
1088 jgs 121
1089 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1090 jgs 147 """
1091     pass
1092 jgs 121
1093 jgs 147 class FailedTimeStepError(Exception):
1094 jgs 149 """
1095     Exception which is thrown if the time step fails because of a step
1096     size that have been choosen to be too large.
1097     """
1098 jgs 147 pass
1099 jgs 121
1100 jgs 147 class SimulationBreakDownError(Exception):
1101 jgs 149 """
1102     Exception which is thrown if the simulation does not manage to
1103     progress in time.
1104     """
1105 jgs 147 pass
1106 jgs 121
1107 jgs 147 class NonPositiveStepSizeError(Exception):
1108 jgs 149 """
1109     Exception which is thrown if the step size is not positive.
1110     """
1111 jgs 147 pass
1112 jgs 149
1113 elspeth 875 class DataSource(object):
1114     """
1115     Class for handling data sources, including local and remote files. This class is under development.
1116     """
1117    
1118 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1119 elspeth 875 self.uri = uri
1120     self.fileformat = fileformat
1121    
1122 gross 918 def toDom(self, esysxml, node):
1123 elspeth 875 """
1124     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1125 gross 918 current XML esysxml.
1126 elspeth 875 """
1127 gross 918 ds = esysxml.createElement('DataSource')
1128     ds.appendChild(esysxml.createDataNode('URI', self.uri))
1129     ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1130 elspeth 875 node.appendChild(ds)
1131    
1132 gross 918 def fromDom(cls, esysxml, node):
1133 gross 920 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1134     fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1135 elspeth 875 ds = cls(uri, fileformat)
1136     return ds
1137    
1138 gross 885 def getLocalFileName(self):
1139     return self.uri
1140    
1141 elspeth 875 fromDom = classmethod(fromDom)
1142    
1143 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