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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/esys2/escript/py_src/modelframe.py revision 121 by jgs, Fri May 6 04:26:16 2005 UTC trunk/escript/py_src/modelframe.py revision 939 by gross, Thu Jan 25 04:23:38 2007 UTC
# Line 1  Line 1 
1  # $Id$  # $Id$
 from types import StringType  
2    
3  class Link:  """
4    """ """  Environment for implementing models in escript
   def __init__(self,object,attribute=None):  
      self.__object=object  
      self.setAttributeName(attribute)  
   
   def setAttributeName(self,name):  
      if not name==None:  
         if not hasattr(self.__object,name):  
            raise AttributeError("Link: object %s has no attribute %s."%(self.__object,name))  
      self.__attribute=name  
   
   def hasAttributeName(self):  
       if self.__attribute==None:  
          return False  
       else:  
          return True  
   
   def __str__(self):  
       if self.hasAttributeName():  
           return "reference to %s of %s"%(self.__attribute,self.__object)  
       else:  
           return "reference to object %s"%self.__object  
   
   def getValue(self,name=None):  
       if not self.hasAttributeName():  
          out=getattr(self.__object,name)  
       else:  
          out=getattr(self.__object,self.__attribute)  
       if callable(out):  
           return out()  
       else:  
           return out  
     
 class Model:  
    """ the Model class provides a framework to run a time-dependent simulation. A Model has a set of parameter which  
        may be fixed or altered by the Model itself or other Models over time.    
   
        The parameters of a models are declared at instantion, e.g.  
   
            m=Model({"message" : "none" })  
   
        creates a Model with parameters p1 and p2 with inital values 1 and 2. Typically a particular model is defined as a subclass of Model:  
   
         class Messenger(Model):  
             def __init__(self):  
                Model.__init__(self,parameters={"message" : "none" })  
5    
6          m=MyModel()  @var __author__: name of author
7    @var __copyright__: copyrights
8         There are various ways how model parameters can be changed:  @var __license__: licence agreement
9    @var __url__: url entry point on documentation
10         1) use object attributes:  @var __version__: version
11    @var __date__: date of the version
12            m.message="Hello World!"  """
13    
14    __author__="Lutz Gross, l.gross@uq.edu.au"
15    __copyright__="""  Copyright (c) 2006 by ACcESS MNRF
16                        http://www.access.edu.au
17                    Primary Business: Queensland, Australia"""
18    __license__="""Licensed under the Open Software License version 3.0
19                 http://www.opensource.org/licenses/osl-3.0.php"""
20    __url__="http://www.iservo.edu.au/esys"
21    __version__="$Revision$"
22    __date__="$Date$"
23    
24    
25    from types import StringType,IntType,FloatType,BooleanType,ListType,DictType
26    from sys import stdout
27    import numarray
28    import operator
29    import itertools
30    
31    # 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    from xml.dom import minidom
38    
39    
40    def all(seq):
41        for x in seq:
42            if not x:
43                return False
44        return True
45    
46    def any(seq):
47        for x in seq:
48            if x:
49                return True
50        return False
51    
52         2) use setParamter method  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            m.setParameters(message="Hello World!")          return vars(module)[name]
63        except KeyError:
64         3) or dictonaries          raise ImportError("Could not import %s from %s" % (name, modulename))
65    
66    class ESySXMLParser(object):
67        """
68        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             d={ message : "Hello World!" }      def getClassPath(self, node):
78             m.setParameters(**d)          type = node.getAttribute("type")
79            if (node.getAttribute("module")):
80                module = node.getAttribute("module")
81         A model executed buy staring the run method of the model:              return importName(module, type)
82            else:
83                return importName("__main__", type)
84    
85        def setLinks(self):
86            for obj_id, link in self.__link_registry:
87                link.target = self.__linkable_object_registry[obj_id]
88    
89        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            m=Messenger()  class Link:
198            m.run()      """
199        A Link makes an attribute of an object callable::
        The run methods marches through time. It first calls the  
        doInitialization() method of the Model to set up the process. In each time step the doStep() method is called  
        to get from the current to the next time step. The step size is defined by calling the getSafeTimeStepSize() method.  
        The time integration process is terminated when the finalize() methods return true. Final the doFinalization() method  
        is called to finalize the process. To implement a particular model a subclass  
        of the Model class is defined. The subclass overwrites the default methods of Model.  
   
        The following class defines a messenger printing in the doStep method what ever the current value of its parameter message is:  
   
        class Messenger(Model):  
             def __init__(self):  
                Model.__init__(self,parameters={"message" : "none" })  
   
             def doInitialization(self):  
                print "I start talking now!"  
   
             def doStep(self,t):  
                print "Message (time %e) : %s "%(t,self.message)  
200    
201              def doFinalization(self):            o.object()
202                 print "I have no more to say!"            o.a=8
203                    l=Link(o,"a")
204         If a instance of the Messenger class is run, it will print the initialization and finalization message only.            assert l()==8
205         This is because the default method for finalize() does always returns True and therefore the transition is       """
206         terminated startcht away.      
207        def __init__(self,target,attribute=None):
208            """
209            Creates a link to the object target. If attribute is given, the link is
210            establised to this attribute of the target.  Otherwise the attribute is
211            undefined.
212            """
213            self.target = target
214            self.attribute = None
215            self.setAttributeName(attribute)
216    
217        def getTarget(self):
218            """
219            returns the target
220            """
221            return self.target
222        def getAttributeName(self):
223            """
224            returns the name of the attribute the link is pointing to
225            """
226            return self.attribute
227        
228        def setAttributeName(self,attribute):
229            """
230            Set a new attribute name to be collected from the target object. The
231            target object must have the attribute with name attribute.
232            """
233            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            self.attribute = attribute
241        
242        def hasDefinedAttributeName(self):
243            """
244            Returns true if an attribute name is set.
245            """
246            return self.attribute != None
247        
248        def __repr__(self):
249            """
250            Returns a string representation of the link.
251            """
252            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            """
259            Returns the value of the attribute of the target object. If the
260            atrribute is callable then the return value of the call is returned.
261            """
262            if name:
263                out=getattr(self.target, name)
264            else:
265                out=getattr(self.target, self.attribute)
266    
267            if callable(out):
268                return out()
269            else:
270                return out
271    
272        def toDom(self, esysxml, node):
273            """
274            C{toDom} method of Link. Creates a Link node and appends it to the
275        current XML esysxml.
276            """
277            link = esysxml.createElement('Link')
278            assert (self.target != None), ("Target was none, name was %r" % self.attribute)
279            link.appendChild(esysxml.createDataNode('Target', esysxml.getLinkableObjectId(self.target)))
280            # 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            link.appendChild(esysxml.createDataNode('Attribute', self.attribute))
284            node.appendChild(link)
285    
286        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            l = cls(None, attribute)
290            esysxml.registerLink(targetid, l)
291            return l
292    
293        fromDom = classmethod(fromDom)
294        
295    class LinkableObject(object):
296        """
297        An object that allows to link its attributes to attributes of other objects
298        via a Link object. For instance::
299              
300               p = LinkableObject()
301               p.x = Link(o,"name")
302               print p.x
303        
304        links attribute C{x} of C{p} to the attribute name of object C{o}.
305    
306        C{p.x} will contain the current value of attribute C{name} of object
307        C{o}.  
308    
309        If the value of C{getattr(o, "name")} is callable, C{p.x} will return
310        the return value of the call.
311        """
312      
313        
314        def __init__(self, id = None, debug=False):
315            """
316        Initializes LinkableObject so that we can operate on Links
317        """
318            self.debug = debug
319            self.__linked_attributes={}
320                    
321         Following example for solving the ODE using a forward euler scheme:      def trace(self, msg):
322            """
323                  u(t=0)=u0      If debugging is on, print the message, otherwise do nothing
324                  u_t=a*u**2       for all 0<t<=ten          """
325            if self.debug:
326        exact solution is given by u(t)=1/(1/u0-a*t)              print "%s: %s"%(str(self),msg)
327        
328        class  Ode1(Model):      def __getattr__(self,name):
329           def __init__(self,**args):          """
330              Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 1. },name="test",debug=True)      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           def doInitialization(self):      """
333               self._tn=0          out = self.getAttributeObject(name)
334            if isinstance(out,Link):
335           def doStep(self,t):              return out()
336               self.u=self.u+(t-self._tn)*self.a*self.u**2          else:
337               self._tn=t              return out
338        
339        def getAttributeObject(self,name):
340            """
341        Return the object stored for attribute name.
342        """
343    
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            if self.__class__.__dict__.has_key(name):
351                return self.__class.__dict__[name]
352    
353            raise AttributeError,"No attribute %s."%name
354        
355        def hasAttribute(self,name):
356            """
357        Returns True if self as attribute name.
358        """
359            return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or  self.__class__.__dict__.has_key(name)
360    
361        def __setattr__(self,name,value):
362            """
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    
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                self.trace("attribute %s is now linked by %s."%(name,value))
376            else:
377                self.__dict__[name] = value
378        
379        def __delattr__(self,name):
380            """
381        Removes the attribute name.
382        """
383    
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    class _ParameterIterator:
392        def __init__(self,parameterset):
393    
394            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    class ParameterSet(LinkableObject):
405        """
406        A class which allows to emphazise attributes to be written and read to XML
407          
408        Leaves of an ESySParameters object can be:
409        
410         - 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         - a numarray object
418             - a list of booleans
419            - any other object (not considered by writeESySXML and writeXML)
420        
421        Example how to create an ESySParameters object::
422        
423            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        
427        This can be accessed as::
428        
429        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        """
439        def __init__(self, parameters=[], **kwargs):
440            """
441        Creates a ParameterSet with parameters parameters.
442        """
443            LinkableObject.__init__(self, **kwargs)
444            self.parameters = set()
445            self.declareParameters(parameters)
446    
447        def __repr__(self):
448            return "<%s %d>"%(self.__class__.__name__,id(self))
449        
450        def declareParameter(self,**parameters):
451            """
452        Declares a new parameter(s) and its (their) initial value.
453        """
454            self.declareParameters(parameters)
455        
456        def declareParameters(self,parameters):
457            """
458        Declares a set of parameters. parameters can be a list, a dictionary
459        or a ParameterSet.
460        """
461            if isinstance(parameters,ListType):
462                parameters = zip(parameters, itertools.repeat(None))
463            if isinstance(parameters,DictType):
464                parameters = parameters.iteritems()
465    
466            for prm, value in parameters:
467                setattr(self,prm,value)
468                self.parameters.add(prm)
469    
470                self.trace("parameter %s has been declared."%prm)
471    
472        def releaseParameters(self,name):
473            """
474        Removes parameter name from the paramameters.
475        """
476            if self.isParameter(name):
477                self.parameters.remove(name)
478                self.trace("parameter %s has been removed."%name)
479        
480        def __iter__(self):
481            """
482        Creates an iterator over the parameter and their values.
483        """
484            return _ParameterIterator(self)
485        
486        def showParameters(self):
487            """
488        Returns a descrition of the parameters.
489        """        
490            out="{"
491            notfirst=False
492            for i,v in self:
493                if notfirst: out=out+","
494                notfirst=True
495                if isinstance(v,ParameterSet):
496                    out="%s\"%s\" : %s"%(out,i,v.showParameters())
497                else:
498                    out="%s\"%s\" : %s"%(out,i,v)
499            return out+"}"
500        
501        def __delattr__(self,name):
502            """
503        Removes the attribute name.
504        """
505            LinkableObject.__delattr__(self,name)
506            try:
507                self.releaseParameter(name)
508            except:
509                pass
510    
511        def toDom(self, esysxml, node):
512            """
513        C{toDom} method of Model class
514        """
515            pset = esysxml.createElement('ParameterSet')
516            pset.setAttribute('type', self.__class__.__name__)
517            pset.setAttribute('module', self.__class__.__module__)
518            esysxml.registerLinkableObject(self, pset)
519            self._parametersToDom(esysxml, pset)
520            node.appendChild(pset)
521    
522        def _parametersToDom(self, esysxml, node):
523            for name,value in self:
524                # convert list to numarray when possible:
525                if isinstance (value, list):
526                    elem_type=-1
527                    for i in value:
528                        if isinstance(i, bool):
529                            elem_type = max(elem_type,0)
530                        if isinstance(i, int) and not isinstance(i, bool):
531                            elem_type = max(elem_type,1)
532                        if isinstance(i, float):
533                            elem_type = max(elem_type,2)
534                    if elem_type == 0: value = numarray.array(value,numarray.Bool)
535                    if elem_type == 1: value = numarray.array(value,numarray.Int)
536                    if elem_type == 2: value = numarray.array(value,numarray.Float)
537    
538                param = esysxml.createElement('Parameter')
539                param.setAttribute('type', value.__class__.__name__)
540    
541                param.appendChild(esysxml.createDataNode('Name', name))
542    
543                val = esysxml.createElement('Value')
544                if isinstance(value,(ParameterSet,Link,DataSource)):
545                    value.toDom(esysxml, val)
546                    param.appendChild(val)
547                elif isinstance(value, numarray.NumArray):
548                    shape = value.getshape()
549                    if isinstance(shape, tuple):
550                        size = reduce(operator.mul, shape)
551                        shape = ' '.join(map(str, shape))
552                    else:
553                        size = shape
554                        shape = str(shape)
555    
556                    arraytype = value.type()
557                    if isinstance(arraytype, numarray.BooleanType):
558                          arraytype_str="Bool"
559                    elif isinstance(arraytype, numarray.IntegralType):
560                          arraytype_str="Int"
561                    elif isinstance(arraytype, numarray.FloatingType):
562                          arraytype_str="Float"
563                    elif isinstance(arraytype, numarray.ComplexType):
564                          arraytype_str="Complex"
565                    else:
566                          arraytype_str=str(arraytype)
567                    numarrayElement = esysxml.createElement('NumArray')
568                    numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
569                    numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
570                    numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
571                        [str(x) for x in numarray.reshape(value, size)])))
572                    val.appendChild(numarrayElement)
573                    param.appendChild(val)
574                elif isinstance(value, list):
575                    param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
576                elif isinstance(value, (str, bool, int, float, type(None))):
577                    param.appendChild(esysxml.createDataNode('Value', str(value)))
578                elif isinstance(value, dict):
579                     dic = esysxml.createElement('dictionary')
580                     if len(value.keys())>0:
581                         dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
582                         dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
583                     for k,v in value.items():
584                        i=esysxml.createElement('item')
585                        i.appendChild(esysxml.createDataNode('key', k))
586                        i.appendChild(esysxml.createDataNode('value', v))
587                        dic.appendChild(i)
588                     param.appendChild(dic)
589                else:
590                    print value
591                    raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
592    
593           def doFinalization(self):              node.appendChild(param)
              print "all done final error = ",abs(self.u-1./(1./3.-self.a*self._tn))  
594    
595           def getSafeTimeStepSize(self):      def fromDom(cls, esysxml, node):
596               return self.dt          # Define a host of helper functions to assist us.
597            def _children(node):
598                """
599                Remove the empty nodes from the children of this node.
600                """
601                ret = []
602                for x in node.childNodes:
603                    if isinstance(x, minidom.Text):
604                        if x.nodeValue.strip():
605                            ret.append(x)
606                    else:
607                        ret.append(x)
608                return ret
609    
610           def finalize(self):          def _floatfromValue(esysxml, node):
611               return self._tn>=self.tend              return float(node.nodeValue.strip())
612    
613         In some cases at a given time step an iteration process has to be performed to get the state of the Model for the next time step. `          def _stringfromValue(esysxml, node):
614         In this case the doStep() method is replaced by a sequance of methods which implements this iterative process.              return str(node.nodeValue.strip())
615         The method then will control the iteration process by initializing the iteration through calling the        
616         doIterationInitialization() method. The iteration is preformed by calling the doIterationStep() method until          def _intfromValue(esysxml, node):
617         the terminate() method returns True. The doIterationFinalization() method is called to end the iteration.              return int(node.nodeValue.strip())
        For a particular model these methods have to overwritten by a suitable subclass without touching the doStep() method.  
618    
619         following example is a modification of the example above. Here an implicit euler scheme is used. in each time step the problem          def _boolfromValue(esysxml, node):
620                          return _boolfromstring(node.nodeValue.strip())
            0= u_{n+1}-u_{n}+a*dt*u_{n+1}**2  
621    
622         has to be solved for u_{n+1}. The Newton scheme is used to solve this non-linear problem.          def _nonefromValue(esysxml, node):
623                return None
624    
625            def _numarrayfromValue(esysxml, node):
626        class  Ode2(Model):              for node in _children(node):
627                    if node.tagName == 'ArrayType':
628                        arraytype = node.firstChild.nodeValue.strip()
629                    if node.tagName == 'Shape':
630                        shape = node.firstChild.nodeValue.strip()
631                        shape = [int(x) for x in shape.split()]
632                    if node.tagName == 'Data':
633                        data = node.firstChild.nodeValue.strip()
634                        data = [float(x) for x in data.split()]
635                return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
636                                        shape)
637          
638            def _listfromValue(esysxml, node):
639                return [x for x in node.nodeValue.split()]
640    
641         def __init__(self,**args):          def _boolfromstring(s):
642             Model.__init__(self,{"tend" : 1., "dt" : 0.1 ,"a" : 10. ,"u" : 1. , "tol " : 1.e-8},"test","bla",None,True)              if s == 'True':
643                    return True
644                else:
645                    return False
646            # Mapping from text types in the xml to methods used to process trees of that type
647            ptypemap = {"Simulation": Simulation.fromDom,
648                        "Model":Model.fromDom,
649                        "ParameterSet":ParameterSet.fromDom,
650                        "Link":Link.fromDom,
651                        "DataSource":DataSource.fromDom,
652                        "float":_floatfromValue,
653                        "int":_intfromValue,
654                        "str":_stringfromValue,
655                        "bool":_boolfromValue,
656                        "list":_listfromValue,
657                        "NumArray":_numarrayfromValue,
658                        "NoneType":_nonefromValue,
659                        }
660    
661            parameters = {}
662            for n in _children(node):
663                ptype = n.getAttribute("type")
664                if not ptypemap.has_key(ptype):
665                   raise KeyError("cannot handle parameter type %s."%ptype)
666    
667                pname = pvalue = None
668                for childnode in _children(n):
669                    if childnode.tagName == "Name":
670                        pname = childnode.firstChild.nodeValue.strip()
671    
672                    if childnode.tagName == "Value":
673                        nodes = _children(childnode)
674                        pvalue = ptypemap[ptype](esysxml, nodes[0])
675    
676                parameters[pname] = pvalue
677    
678            # Create the instance of ParameterSet
679            o = cls(debug=esysxml.debug)
680            o.declareParameters(parameters)
681            esysxml.registerLinkableObject(o, node)
682            return o
683        
684        fromDom = classmethod(fromDom)
685    
686        def writeXML(self,ostream=stdout):
687            """
688        Writes the object as an XML object into an output stream.
689        """
690            esysxml=ESySXMLCreator()
691            self.toDom(esysxml, esysxml.getRoot())
692            ostream.write(esysxml.toprettyxml())
693        
694    class Model(ParameterSet):
695        """
696        A Model object represents a processess marching over time until a
697        finalizing condition is fullfilled. At each time step an iterative
698        process can be performed and the time step size can be controlled. A
699        Model has the following work flow::
700              
701              doInitialization()
702              while not terminateInitialIteration(): doInitializationiStep()
703              doInitialPostprocessing()
704              while not finalize():
705                   dt=getSafeTimeStepSize(dt)
706                   doStepPreprocessing(dt)
707                   while not terminateIteration(): doStep(dt)
708                   doStepPostprocessing(dt)
709              doFinalization()
710    
711        where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
712        C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
713        C{doFinalization} are methods of the particular instance of a Model. The
714        default implementations of these methods have to be overwritten by the
715        subclass implementing a Model.
716        """
717    
718        UNDEF_DT=1.e300
719    
720        def __init__(self,parameters=[],**kwargs):
721            """
722        Creates a model.
723    
724            Just calls the parent constructor.
725            """
726            ParameterSet.__init__(self, parameters=parameters,**kwargs)
727    
728        def __str__(self):
729           return "<%s %d>"%(self.__class__.__name__,id(self))
730    
731    
732        def doInitialization(self):
733            """
734        Initializes the time stepping scheme.  
735        
736        This function may be overwritten.
737        """
738            pass
739        def doInitialStep(self):
740            """
741        performs an iteration step in the initialization phase
742    
743        This function may be overwritten.
744        """
745            pass
746    
747        def terminateInitialIteration(self):
748            """
749        Returns True if iteration at the inital phase is terminated.
750        """
751            return True
752    
753        def doInitialPostprocessing(self):
754            """
755        finalises the initialization iteration process
756    
757        This function may be overwritten.
758        """
759            pass
760        
761        def getSafeTimeStepSize(self,dt):
762            """
763        Returns a time step size which can safely be used.
764    
765            C{dt} gives the previously used step size.
766    
767            This function may be overwritten.
768        """
769            return self.UNDEF_DT
770        
771        def finalize(self):
772            """
773        Returns False if the time stepping is finalized.
774        
775        This function may be overwritten.
776        """
777            return False
778          
779        def doFinalization(self):
780            """
781        Finalizes the time stepping.
782        
783        This function may be overwritten.
784        """
785            pass
786        
787        def doStepPreprocessing(self,dt):
788            """
789        Sets up a time step of step size dt.
790        
791        This function may be overwritten.
792        """
793            pass
794        
795        def doStep(self,dt):
796            """
797        Executes an iteration step at a time step.
798    
799            C{dt} is the currently used time step size.
800    
801            This function may be overwritten.
802        """
803            pass
804        
805        def terminateIteration(self):
806            """
807        Returns True if iteration on a time step is terminated.
808        """
809            return True
810    
811         def doInitialization(self):        
812             self.__tn=0      def doStepPostprocessing(self,dt):
813            """
814        finalises the time step.
815    
816            dt is the currently used time step size.
817    
818            This function may be overwritten.
819        """
820            pass
821    
822        def toDom(self, esysxml, node):
823            """
824        C{toDom} method of Model class
825        """
826            pset = esysxml.createElement('Model')
827            pset.setAttribute('type', self.__class__.__name__)
828            pset.setAttribute('module', self.__class__.__module__)
829            esysxml.registerLinkableObject(self, pset)
830            node.appendChild(pset)
831            self._parametersToDom(esysxml, pset)
832        
833    class Simulation(Model):
834        """
835        A Simulation object is special Model which runs a sequence of Models.
836    
837        The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
838        C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
839        C{doFinalization} are executing the corresponding methods of the models in
840        the simulation.
841        """
842        
843        FAILED_TIME_STEPS_MAX=20
844        MAX_ITER_STEPS=50
845        MAX_CHANGE_OF_DT=2.
846        
847        def __init__(self, models=[], **kwargs):
848            """
849        Initiates a simulation from a list of models.
850        """
851            super(Simulation, self).__init__(**kwargs)
852            for m in models:
853                if not isinstance(m, Model):
854                     raise TypeError("%s is not a subclass of Model."%m)
855            self.__models=[]
856            for i in range(len(models)):
857                self[i] = models[i]
858                
859    
860         def doIterationInitialization(self,t):      def __repr__(self):
861              self.__iter=0          """
862              self.u_last=self.u                      Returns a string representation of the Simulation.
863              self.current_dt=t-self.tn          """
864              self.__tn=t          return "<Simulation %r>" % self.__models
865    
866         def doIterationStep(self):      def __str__(self):
867            self.__iter+=1          """
868            self.u_old=self.u          Returning Simulation as a string.
869            self.u=(self.current_dt*self.a*self.u**2-self.u_last)/(2*self.current_dt*self.a*self.u-1.)          """
870            return "<Simulation %d>"%id(self)
871         def terminate(self):      
872             return abs(self.u_old-self.u)<self.tol*abs(self.u)      def iterModels(self):
873            """
874         def doIterationFinalization(self)      Returns an iterator over the models.
875             print "all done"      """
876            return self.__models
877         def getSafeTimeStepSize(self):      
878             return self.dt      def __getitem__(self,i):
879            """
880         def finalize(self):      Returns the i-th model.
881              return self.__tn>self.tend      """
882            return self.__models[i]
883         A model can be composed from submodels. Submodels are treated as model parameters. If a model parameter is set or a value of      
884         a model parameter is requested, the model will search for this parameter its submodels in the case the model does not have this      def __setitem__(self,i,value):
885         parameter itself. The order in which the submodels are searched is critical. By default a Model initializes all its submodels,          """
886         is finalized when all its submodels are finalized and finalizes all its submodels. In the case an iterative process is applied      Sets the i-th model.
887         on a particular time step the iteration is initialized for all submodels, then the iteration step is performed for each submodel      """
888         until all submodels indicate termination. Then the iteration is finalized for all submodels. Finally teh doStop() method for all          if not isinstance(value,Model):
889         submethods is called.              raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
890            for j in range(max(i-len(self.__models)+1,0)):
891         Here we are creating a model which groups ab instantiation of the Ode2 and the Messenger Model              self.__models.append(None)
892            self.__models[i]=value
893         o=Ode2()      
894         m=Messenger()      def __len__(self):
895         om=Model(submodels=[o,m],debug=True)          """
896         om.dt=0.01      Returns the number of models.
897         om.u=1.      """
898         m.message="it's me!"          return len(self.__models)
899         om.run()  
900        def getAllModels(self):
901         Notice that dt and u are parameters of class Ode2 and message is a parameter of the Messenger class. The Model formed from these models          """
902         automatically hand the assignment of new values down to the submodel. om.run() starts this combined model where now the soStep() method          returns a list of all models used in the Simulation including subsimulations
903         of the Messenger object printing the value of its parameter message together with a time stamp is executed in each time step introduced          """
904         by the Ode2 model.          out=[]
905            for m in self.iterModels():
906                if isinstance(m, Simulation):
907                   out+=m.getAllModels()
908                else:
909                   out.append(m)
910            return list(set(out))
911    
912         A parameter of a Model can be linked to an attribute of onother object, typically an parameter of another Model object.          def checkModelLinks(self, models):
913            """
914            returns a list of (model,parameter, target model ) if the the parameter of model
915            is linking to the target_model which is not in list of models.
916            """
917            out=[]
918            for m in self.iterModels():
919                if isinstance(m, Simulation):
920                   out+=[ (m,) + f for f in  m.checkModelLinks(models) ]
921                else:
922                  for p in m:
923                     if isinstance(p[1], Link):
924                        l_m=p[1].getTarget()
925                        if isinstance(l_m, Model) and not l_m in models: out.append( (m,p[0],l_m) )
926            return out
927    
928        
929        def getSafeTimeStepSize(self,dt):
930            """
931        Returns a time step size which can safely be used by all models.
932    
933            This is the minimum over the time step sizes of all models.
934        """
935            out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
936            return out
937        
938        def doInitialization(self):
939            """
940        Initializes all models.
941        """
942            self.n=0
943            self.tn=0.
944            for o in self.iterModels():
945                 o.doInitialization()
946        def doInitialStep(self):
947            """
948        performs an iteration step in the initialization step for all models
949        """
950            iter=0
951            while not self.terminateInitialIteration():
952                if iter==0: self.trace("iteration for initialization starts")
953                iter+=1
954                self.trace("iteration step %d"%(iter))
955                for o in self.iterModels():
956                     o.doInitialStep()
957                if iter>self.MAX_ITER_STEPS:
958                     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
959            self.trace("Initialization finalized after %s iteration steps."%iter)
960    
961        def doInitialPostprocessing(self):
962            """
963        finalises the initialization iteration process for all models.
964        """
965            for o in self.iterModels():
966                o.doInitialPostprocessing()
967        def finalize(self):
968            """
969        Returns True if any of the models is to be finalized.
970        """
971            return any([o.finalize() for o in self.iterModels()])
972                
973        def doFinalization(self):
974            """
975        finalises the time stepping for all models.
976        """
977            for i in self.iterModels(): i.doFinalization()
978            self.trace("end of time integation.")
979        
980        def doStepPreprocessing(self,dt):
981            """
982        Initializes the time step for all models.
983        """
984            for o in self.iterModels():
985                o.doStepPreprocessing(dt)
986        
987        def terminateIteration(self):
988            """
989        Returns True if all iterations for all models are terminated.
990        """
991            out=all([o.terminateIteration() for o in self.iterModels()])
992            return out
993    
994        def terminateInitialIteration(self):
995            """
996        Returns True if all initial iterations for all models are terminated.
997        """
998            out=all([o.terminateInitialIteration() for o in self.iterModels()])
999            return out
1000                
1001         which is comprised by a set of submodels.      def doStepPostprocessing(self,dt):
1002         The simulation is run through its run method which in the simplest case has the form:          """
1003        finalises the iteration process for all models.
1004            s=Model()      """
1005            s.run()          for o in self.iterModels():
1006                o.doStepPostprocessing(dt)
1007         The run has an initializion and finalization phase. The latter is called if all submodels are to be finalized. The          self.n+=1
1008         simulation is processing in time through calling the stepForward methods which updates the observables of each submodel.          self.tn+=dt
1009         A time steps size which is save for all submodel is choosen.      
1010        def doStep(self,dt):
1011         At given time step an iterative process may be performed to make sure that all observables are consistent across all submodels.          """
1012         In this case, similar the time dependence, an initialization and finalization of the iteration is performed.      Executes the iteration step at a time step for all model::
1013    
1014         A Model has input and output parameters where each input parameter can be constant, time dependent or may depend on an              self.doStepPreprocessing(dt)
1015         output parameter of another model or the model itself. To create a parameter name of a model and to              while not self.terminateIteration():
1016         assign a value to it one can use the statement              for all models:
1017                self.doStep(dt)
1018             model.name=object                  self.doStepPostprocessing(dt)
1019            """
1020            self.iter=0
1021         At any time the current value of the parameter name can be obtained by          while not self.terminateIteration():
1022                if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
1023                 value=model.name              self.iter+=1
1024                self.trace("iteration step %d"%(self.iter))
1025         If the object that has been assigned to the paramter/attribute name has the attribute/parameter name isself the current value of this              for o in self.iterModels():
1026         attribute of the object is returned (e.g. for model.name=object where object has an attribute name, the statement value=model.name whould assign                    o.doStep(dt)
1027         the value object.name to value.). If the name of the parameters of a model and an object don't match the setParameter method of model can be used. So          if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
1028    
1029             model.setParameter(name,object,name_for_object)      def run(self,check_point=None):
1030            """
1031         links the parameter name of model with the parameter name_for_object of object.      Run the simulation by performing essentially::
1032        
1033            self.doInitialization()
1034                while not self.terminateInitialIteration(): self.doInitialStep()
1035                self.doInitialPostprocessing()
1036            while not self.finalize():
1037                dt=self.getSafeTimeStepSize()
1038                self.doStep(dt)
1039                if n%check_point==0:
1040                self.writeXML()
1041            self.doFinalization()
1042    
1043            If one of the models in throws a C{FailedTimeStepError} exception a
1044        new time step size is computed through getSafeTimeStepSize() and the
1045        time step is repeated.
1046      
1047            If one of the models in throws a C{IterationDivergenceError}
1048        exception the time step size is halved and the time step is repeated.
1049    
1050         The run method initiates checkpointing (it is not clear how to do this yet)          In both cases the time integration is given up after
1051     =====      C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1052                        """
1053     """          # check the completness of the models:
1054     # step size used in case of an undefined value for the step size          # first a list of all the models involved in the simulation including subsimulations:
1055     UNDEF_DT=1.e300          #
1056            missing=self.checkModelLinks(self.getAllModels())
1057     def __init__(self,submodels=[],parameters={},name="model",description="none",check_pointing=None,debug=False):          if len(missing)>0:
1058        """initiates a model from a list of submodels. """              msg=""
1059        self.setDebug(debug)              for l in missing:
1060        self.__check_pointing=check_pointing                   msg+="\n\t"+str(l[-1])+" at "+str(self)
1061        self.__parameters={}                   for i in xrange(len(l)-1): msg+="."+str(l[i])
1062        self.setName(name)              raise MissingLink("link targets missing in the Simulation: %s"%msg)
1063        self.setDescription(description)          #==============================
1064        self.declareParameter(**parameters)          self.doInitialization()
1065        # get the models defined in parameters:          self.doInitialStep()
1066        self.__submodels=[]          self.doInitialPostprocessing()
1067        # submodels==None means no submodels used:          dt=self.UNDEF_DT
1068        if submodels==None:          while not self.finalize():
1069           pass              step_fail_counter=0
1070        # no submodel list given means all submodels are used as defined by the parameters dictionary:              iteration_fail_counter=0
1071        elif len(submodels)==0:              if self.n==0:
1072              for i in parameters.keys():                  dt_new=self.getSafeTimeStepSize(dt)
                 if isinstance(parameters[i],Model): self.__submodels.append(i)  
       # submodel list of strings and Models is given, submodels defines the order in which the  
       # submodels are processed. if new models are found in the list they are added to the parameter dictionary.  
       else:  
          c=0  
          for i in submodels:  
             if isinstance(i,StringType):  
               m=self.getParameter(i)  
               if not isinstance(m,Model):  
                  raise ValueError,"submodel %s is not a model."%i  
1073              else:              else:
1074                 if not isinstance(i,Model):                  dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1075                   raise ValueError,"submodel list does contain item which is not a Model class object."              self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1076                 m=i              end_of_step=False
1077                 i="__submodel%d__"%c              while not end_of_step:
1078                 self.declareParameter(**{i : m})                 end_of_step=True
1079                 c+=1                 if not dt_new>0:
1080              self.__submodels.append(i)                    raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1081              if self.debug(): print "%s: model %s is added as parameter %s."%(self,m,i)                 try:
1082        if len(self.__submodels)>0 and self.debug(): print "%s: model ordering is %s"%(self,self.__submodels)                    self.doStepPreprocessing(dt_new)
1083     def setSubmodelOrder(submodels=[]):                    self.doStep(dt_new)
1084        """sets a new ordering for submodels"""                    self.doStepPostprocessing(dt_new)
1085                         except IterationDivergenceError:
1086                          dt_new*=0.5
1087     #                    end_of_step=False
1088     # some basic fuctions:                    iteration_fail_counter+=1
1089     #                    if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1090     def debugOn(self):                             raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1091        """sets debugging to on"""                    self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1092        self.__debug=True                 except FailedTimeStepError:
1093     def debugOff(self):                    dt_new=self.getSafeTimeStepSize(dt)
1094        """sets debugging to off"""                    end_of_step=False
1095        self.__debug=False                    step_fail_counter+=1
1096     def debug(self):                    self.trace("Time step is repeated with new time step size %s."%dt_new)
1097        """returns True if debug mode is set to on"""                    if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1098        return self.__debug                          raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1099     def setDebug(self,flag=False):              dt=dt_new
1100        """sets debugging to flag"""              if not check_point==None:
1101        if flag:                  if n%check_point==0:
1102           self.debugOn()                      self.trace("check point is created.")
1103        else:                      self.writeXML()
1104           self.debugOff()          self.doFinalization()
1105     def setDebug(self,flag=False):  
1106        """sets debugging to flag"""  
1107        if flag:      def toDom(self, esysxml, node):
1108           self.debugOn()          """
1109        else:      C{toDom} method of Simulation class.
1110           self.debugOff()      """
1111     # name and description handling          simulation = esysxml.createElement('Simulation')
1112     def __str__(self):          esysxml.registerLinkableObject(self, simulation)
1113         """returns the name of the model"""          for rank, sim in enumerate(self.iterModels()):
1114         return self.getName()              component = esysxml.createElement('Component')
1115                component.setAttribute('rank', str(rank))
1116     def getName(self):              sim.toDom(esysxml, component)
1117         """returns the name of the model"""              simulation.appendChild(component)
1118         return self.__name          node.appendChild(simulation)
1119    
1120     def getFullName(self):  
1121         """returns the full name of the model including all the names of the submodels"""      def fromDom(cls, esysxml, node):
1122         out=str(self)+"("          sims = []
1123         notfirst=False          for n in node.childNodes:
1124         for i in self.__submodels:              if isinstance(n, minidom.Text):
1125              if notfirst: out=out+","                  continue
1126              out=out+i.getFullName()              sims.append(esysxml.getComponent(n))
1127              notfirst=True          sims.sort(cmp=_comp)
1128         return out+")"          sim=cls([s[1] for s in sims], debug=esysxml.debug)
1129            esysxml.registerLinkableObject(sim, node)
1130     def setName(self,name):          return sim
1131         """sets the name of the model"""  
1132         self.__name=name      fromDom = classmethod(fromDom)
1133    
1134     def setDescription(self,description="none"):  def _comp(a,b):
1135         """sets new description"""      if a[0]<a[1]:
1136         self.__description=description        return 1
1137         if self.debug(): print "%s: description is set to %s."%(self,description)      elif a[0]>a[1]:
1138     def getDescription(self):        return -1
1139         """returns the description of the model"""      else:
1140         return self.__description        return 0
    #  
    #    parameter/attribute handling:  
    #  
    def declareParameter(self,**parameters):  
       """declares a new parameter and its inital value."""  
       for prm in parameters.keys():  
          if prm in self.__dict__.keys():  
              raise ValueError,"object attribute %s of %s cannot be used as a model parameter."%(prm,self)  
          self.__parameters[prm]=parameters[prm]  
          if self.debug(): print "%s: parameter %s has been declared."%(self,prm)  
   
   
   
    def showParameters(self):  
       """returns a descrition of the parameters"""  
       out=""  
       notfirst=False  
       for i in self.__parameters:  
           if notfirst: out=out+","  
           notfirst=True  
           out="%s%s=%s"%(out,i,self.__parameters[i])  
       return out  
   
   
    def deleteParameter(self,name):  
       """removes parameter name from the model"""  
       raise IllegalParameterError("Cannot delete parameter %s."%name)  
   
    def getParameter(self,name):  
       """returns the value of parameter name. If the parameter is not declared in self, the submodels are searched.  
          if the parameter is a Link, the current value of the obejective is returned."""  
       if self.__parameters.has_key(name):  
           if isinstance(self.__parameters[name],Link):  
              out=self.__parameters[name].getValue(name)  
           else:  
              out=self.__parameters[name]  
       else:  
           out=None  
           for i in self.__submodels:  
              try:  
                 out=self.__parameters[i].getParameter(name)  
              except IllegalParameterError:  
                 pass  
           if out==None: raise IllegalParameterError("Cannot find parameter %s."%name)  
       return out  
   
    def setParameter(self,**parameters):  
       """sets parameter name to value. If the initial value for the parameter is a Model, the new value has to be a Model."""  
       for name in parameters.keys():  
          if self.__parameters.has_key(name):  
             if not isinstance(parameters[name],Model) and isinstance(self.__parameters[name],Model):  
                 raise ValueError,"%s: parameter %s can assigned to a Model object only."%(self,name)  
             if isinstance(parameters[name],Model) and not isinstance(self.__parameters[name],Model):  
                 raise ValueError,"%s: parameter %s is not declared as a Model."%(self,name)  
             self.__parameters[name]=parameters[name]  
             if isinstance(self.__parameters[name],Link):  
                  if not self.__parameters[name].hasAttributeName(): self.__parameters[name].setAttributeName(name)  
             if self.debug(): print "%s: parameter %s has now value %s"%(self,name,self.__parameters[name])  
          else:  
             set=False  
             for i in self.__submodels:  
                 try:  
                    self.__parameters[i].setParameter(**{name : parameters[name]})  
                    set=True  
                 except IllegalParameterError:  
                     pass  
             if not set: raise IllegalParameterError("%s: Attempt to set undeclared parameter %s."%(self,name))  
   
    def hasParameter(self,name):  
       """returns True if self or one of the submodels has parameter name"""  
       if self.__parameters.has_key(name):  
          out=True  
       else:  
          out=False  
          for i in self.__submodels: out= out or self.__parameters[i].hasParameter(name)  
       return out  
   
    def checkParameter(self,name):  
       """checks if self has the parameter name. Otherewise ParameterError is thrown."""  
       if not self.hasParameter(name):  
            raise ParameterError("%s has no parameter %s."%(str(self),name))  
     
    def __getattr__(self,name):  
       """returns the value for attribute name. If name is in the Link list, the corresponding attribute is returned."""  
       if self.__dict__.has_key(name):  
          return self.__dict__[name]  
       elif self.__dict__.has_key("_Model__parameters") and self.__dict__.has_key("_Model__submodels"):  
          return self.getParameter(name)  
       else:  
          raise AttributeError,"No attribute %s."%name  
   
    def __setattr__(self,name,value):  
       """returns the value for attribute name."""  
       if self.__dict__.has_key("_Model__parameters") and self.__dict__.has_key("_Model__submodels"):  
          if self.hasParameter(name):  
             self.setParameter(**{ name : value })  
          else:  
             self.__dict__[name]=value  
       else:  
          self.__dict__[name]=value  
   
    def __delattr__(self,name):  
       """removes the attribute name."""  
       if self.__dict__.has_key(name):  
          del self.__dict__[name]  
       elif self.__dict__.has_key("_Model__parameters"):  
          self.deleteParameter(name)  
       else:  
          raise AttributeError,"No attribute %s."%name  
   
    #  
    #    submodel handeling:  
    #  
    def doInitializationOfSubmodels(self):  
       """initializes the time stepping for all submodels."""  
       for i in self.__submodels: self.getParameter(i).doInitialization()  
   
    def getSafeTimeStepSizeFromSubmodels(self):  
       """returns a time step size which can savely be used by all submodels. To avoid a big increase in the step size,  
          the new step size is restricted to the double of the precious step size."""  
       out=None  
       for i in self.__submodels:  
           dt=self.getParameter(i).getSafeTimeStepSize()  
           if not dt==None:  
               if out==None:  
                  out=dt  
               else:  
                  out=min(out,dt)  
       return out  
   
    def doStepOfSubmodels(self,t):  
       """executes the time step for each submodel"""  
       for i in self.__submodels: self.getParameter(i).doStep(t)  
   
    def finalizeAllSubmodels(self):  
       """returns True if all submodels can be finalized"""  
       out=True  
       for i in self.__submodels: out = out and self.getParameter(i).finalize()  
       return out  
         
    def doFinalizationOfSubmodels(self):  
       """finalalizes the time stepping for each of the submodels."""  
       for i in self.__submodels: self.getParameter(i).doFinalization()  
   
    def doIterationInitializationOfSubmodels(self,t):  
       """initializes the iteration for each of the submodels."""  
       for i in self.__submodels: self.getParameter(i).doIterationInitialization(t)  
   
    def doIterationStepOfSubmodels(self):  
       """executes the iteration step at time step for each submodel"""  
       for i in self.__submodels: self.getParameter(i).doIterationStep()  
   
    def terminateAllSubmodels(self):  
       """returns True if all iterations for all submodels are terminated."""  
       out=True  
       for i in self.__submodels: out = out and self.getParameter(i).terminate()  
       return out  
         
    def doIterationFinalizationOfSubmodels(self):  
       """finalalizes the iteration process for each of the submodels."""  
       for i in self.__submodels: self.getParameter(i).doIterationFinalization()  
   
    def checkPointSubmodels(self):  
       """performs check pointing for each submodel"""  
       for i in self.__submodels: self.getParameter(i).checkPoint()  
   
    #  
    #   these methods control the time stepping  
    #    
    def doInitialization(self):  
       """initializes the time stepping"""  
       self.doInitializationOfSubmodels()  
   
    def getSafeTimeStepSize(self):  
       """returns a time step size which can savely be used"""  
       return self.getSafeTimeStepSizeFromSubmodels()  
   
    def doStep(self,t):  
       """executes the time step by first iterating over time step t and then step forward"""  
       # run iteration on simulation until terminated:  
       self.doIterationInitialization(t)  
       while not self.terminate(): self.doIterationStep()  
       self.doIterationFinalization()  
       self.doStepOfSubmodels(t)  
   
    def finalize(self):  
       """returns True if all submodels are to be finalized"""  
       return self.finalizeAllSubmodels()  
         
    def doFinalization(self):  
       """finalizes the time stepping."""  
       self.doFinalizationOfSubmodels()  
    #  
    #   methods deal with iterations:  
    #  
    def doIterationInitialization(self,t):  
       """initializes the iteration on a time step"""  
       self.__iter=0  
       if self.debug(): print "%s: iteration starts"%self  
       self.doIterationInitializationOfSubmodels(t)  
   
    def doIterationStep(self):  
       """executes the iteration step"""  
       self.__iter+=1  
       if self.debug(): print "%s: iteration step %d"%(self,self.__iter)  
       try:  
          self.doIterationStepOfSubmodels()  
       except IterationDivergenceError,e:  
          raise IterationDivergenceError("divergence at time step %s in iteration step %s by reason: \n%s."%(self.__n,self.__iter,e.value))  
   
    def terminate(self):  
       """returns True if time steping is terminated"""  
       return self.terminateAllSubmodels()  
         
    def doIterationFinalization(self):  
       """finalalizes the iteration process."""  
       self.doIterationFinalizationOfSubmodels()  
       if self.debug(): print "%s: iteration finalized after %s step"%(self,self.__iter)  
    #  
    #   sum other method:  
    #  
    def checkPoint(self):  
       """performs check pointing for each submodel"""  
       if not self.__check_pointing==None:  
          if self.__n%self.__check_pointing==0: self.checkPointsSubmodels()  
   
    def run(self):  
       """After check_pointing time steps the model will start to create checkpoint files for each of the submodels"""  
       self.__tn=0.  
       self.__n=0  
       self.__dt=None  
       self.doInitialization()  
       while not self.finalize():  
          self.__n+=1  
          self.__dt=self.getSafeTimeStepSize()  
          if self.__dt==None: self.__dt=self.UNDEF_DT  
          if self.debug(): print "%s: %d. time step %e (step size %e.)"%(self,self.__n,self.__tn+self.__dt,self.__dt)  
          endoftimestep=False  
          while not endoftimestep:  
               endoftimestep=True  
               try:  
                  self.doStep(self.__tn+self.__dt)  
               except FailedTimeStepError:  
                  self.__dt=self.getSafeTimeStepSize()  
                  if self.__dt==None: self.__dt=self.UNDEF_DT  
                  endoftimestep=False  
                  if self.debug(): print "%s: time step is repeated with new step size %e."%(self,self.__dt)  
               except IterationDivergenceError:  
                  self.__dt*=0.5  
                  endoftimestep=False  
                  if self.debug(): print "%s: iteration failes. time step is repeated with new step size %e."%(self,self.__dt)  
          self.checkPoint()  
          self.__tn+=self.__dt  
       self.doFinalization()  
1141    
1142  class IterationDivergenceError(Exception):  class IterationDivergenceError(Exception):
1143      """excpetion which should be thrown if an iteration at a time step fails"""      """
1144        Exception which is thrown if there is no convergence of the iteration
1145        process at a time step.
1146    
1147        But there is a chance that a smaller step could help to reach convergence.
1148        """
1149      pass      pass
1150    
1151  class FailedTimeStepError(Exception):  class FailedTimeStepError(Exception):
1152      """excpetion which should be thrown if the time step fails because of a step size that have been choosen to be to large"""      """
1153        Exception which is thrown if the time step fails because of a step
1154        size that have been choosen to be too large.
1155        """
1156      pass      pass
1157    
1158  class IllegalParameterError(Exception):  class SimulationBreakDownError(Exception):
1159      """excpetion which is thrown if model has not the desired parameter"""      """
1160        Exception which is thrown if the simulation does not manage to
1161        progress in time.
1162        """
1163      pass      pass
1164    
1165    class NonPositiveStepSizeError(Exception):
1166        """
1167        Exception which is thrown if the step size is not positive.
1168        """
1169        pass
1170    
1171  if __name__=="__main__":  class MissingLink(Exception):
1172     class Messenger(Model):      """
1173        def __init__(self):      Exception thrown when a link is missing
1174           Model.__init__(self,parameters={"message" : "none" },name="messenger")      """
1175        pass
       def doInitialization(self):  
          print "I start talking now!"  
   
       def doStep(self,t):  
          print "Message (time %e) : %s "%(t,self.message)  
   
       def doFinalization(self):  
          print "I have no more to say!"  
     
    # explicit scheme  
    class  Ode1(Model):  
       def __init__(self,**args):  
            Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 1. , "message" : "none" },name="Ode1",debug=True)  
   
       def doInitialization(self):  
            self._tn=0  
   
       def doStep(self,t):  
            self.u=self.u+(t-self._tn)*self.a*self.u**2  
            self._tn=t  
   
       def doFinalization(self):  
            self.message="current error = %e"%abs(self.u-1./(1./3.-self.a*self._tn))  
            print self.message  
   
       def getSafeTimeStepSize(self):  
            return self.dt  
   
       def finalize(self):  
            return self._tn>=self.tend  
    # explicit scheme  
    class  Ode2(Model):  
   
        def __init__(self,**args):  
            Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 10000. },name="Ode2",debug=True)  
            self.declareParameter(tol=1.e-8,message="none")  
             
   
        def doInitialization(self):  
            self._tn=0  
            self._iter=0  
   
        def doIterationInitialization(self,t):  
             self._iter=0  
             self._u_last=self.u              
             self._dt=t-self._tn  
             self._tn=t  
   
        def doIterationStep(self):  
           self._iter+=1  
           self._u_old=self.u  
           self.u=(self._dt*self.a*self.u**2-self._u_last)/(2*self._dt*self.a*self.u-1.)  
   
        def terminate(self):  
           if self._iter<1:  
               return False  
           else:  
              return abs(self._u_old-self.u)<self.tol*abs(self.u)  
   
        def doIterationFinalization(self):  
            self.message="current error = %e"%abs(self.u-1./(1-self.a*self._tn))  
            print self.message  
   
        def getSafeTimeStepSize(self):  
            return self.dt  
   
        def finalize(self):  
             return self._tn>=self.tend  
   
    # a simple model with paramemter tend, dt, p1, p2, and p3  
    class Test1(Model):  
   
        def __init__(self,**args):  
            Model.__init__(self,{"tend" : 1., "dt" : 0.1 ,"p1" : 0 ,"p2" : 0 ,"p3" : 0 },"test","bla",None,True)  
            self.setParameters(args)  
   
        def doInitialization(self):  
            self.__tn=0  
            self.__n=0  
   
        def doStep(self,t):  
            self.p3=self.p1+t*self.p2  
            self.__tn=t  
            print "test1 set the value out1 to ",self.p3  
   
        def doFinalization(self):  
            pass  
   
        def getSafeTimeStepSize(self):  
            return self.dt  
   
        def finalize(self):  
             return self._tn>self.tend  
   
   
    class Test2(Model):  
   
        def __init__(self):  
            Model.__init__(self,{"q1": None},"test2","",None,True)  
   
   
        def doInitialization(self):  
            print "the whole thing starts"  
   
        def doStep(self,t):  
            print "test2 things that out1 is now ",self.out1  
   
        def doFinalization(self):  
            print "all done"  
   
        def finalize(self):  
             return True  
1176    
1177     class Test12(Model):  class DataSource(object):
1178       """model build from two models in a transperent way"""      """
1179       def __init__(self):      Class for handling data sources, including local and remote files. This class is under development.
1180           Model.__init__(self,{"sm1": None, a : 0, "sm2": None},"test2","",None,True)      """
1181           self.setExecutionOrder(["sm2","sm1"])  
1182        def __init__(self, uri="file.ext", fileformat="unknown"):
1183     # test messenger          self.uri = uri
1184     m=Messenger()          self.fileformat = fileformat
1185     m.run()  
1186     # ode1            def toDom(self, esysxml, node):
1187     o=Ode1()          """
1188     o.dt=0.001          C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1189     o.u=3.      current XML esysxml.
1190     o.run()          """
1191     # ode1          ds = esysxml.createElement('DataSource')
1192     o=Ode2()          ds.appendChild(esysxml.createDataNode('URI', self.uri))
1193     o.dt=0.01          ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1194     o.a=0.1          node.appendChild(ds)
1195     o.u=1.  
1196     o.run()      def fromDom(cls, esysxml, node):
1197     # and they are linked together:          uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1198     o=Ode2()          fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1199     m=Messenger()          ds = cls(uri, fileformat)
1200     om=Model(submodels=[o,m],debug=True)          return ds
1201     om.dt=0.01  
1202     om.u=1.      def getLocalFileName(self):
1203     m.message=Link(o)          return self.uri
1204     om.run()  
1205     print om.showParameters()      fromDom = classmethod(fromDom)
1206     1/0      
1207    # vim: expandtab shiftwidth=4:
    t=Test1()  
    t.tend=1.  
    t.dt=0.25  
    t.in1=1.  
    t.in2=3.  
    t.run()  
    # and a coupled problem:  
    t2=Test2()  
    t2.out1=Link(t)  
    Model([t,t2],debug=True).run()  

Legend:
Removed from v.121  
changed lines
  Added in v.939

  ViewVC Help
Powered by ViewVC 1.1.26