ViewVC logotype

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

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

revision 121 by jgs, Fri May 6 04:26:16 2005 UTC revision 147 by jgs, Fri Aug 12 01:45:47 2005 UTC
# Line 1  Line 1 
1  # $Id$  # $Id$
 from types import StringType  
3  class Link:  from types import StringType,IntType,FloatType,BooleanType,ListType,DictType
4    """ """  from sys import stdout
5    def __init__(self,object,attribute=None):  import itertools
6       self.__object=object  # import modellib  temporarily removed!!!
7       self.setAttributeName(attribute)  
8    # import the 'set' module if it's not defined (python2.3/2.4 difference)
9    def setAttributeName(self,name):  try:
10       if not name==None:      set
11          if not hasattr(self.__object,name):  except NameError:
12             raise AttributeError("Link: object %s has no attribute %s."%(self.__object,name))      from sets import Set as set
13       self.__attribute=name  
14    from xml.dom import minidom
15    def hasAttributeName(self):  
16        if self.__attribute==None:  def dataNode(document, tagName, data):
17           return False      """
18        else:      dataNodes are the building blocks of the xml documents constructed in
19           return True      this module. document is the current xml document, tagName is the
20        associated xml tag, and data is the values in the tag.
21    def __str__(self):      """
22        if self.hasAttributeName():      t = document.createTextNode(str(data))
23            return "reference to %s of %s"%(self.__attribute,self.__object)      n = document.createElement(tagName)
24        else:      n.appendChild(t)
25            return "reference to object %s"%self.__object      return n
27    def getValue(self,name=None):  def esysDoc():
28        if not self.hasAttributeName():      """
29           out=getattr(self.__object,name)      Global method for creating an instance of an EsysXML document.
30        else:      """
31           out=getattr(self.__object,self.__attribute)      doc = minidom.Document()
32        if callable(out):      esys = doc.createElement('ESys')
33            return out()      doc.appendChild(esys)
34        else:      return doc, esys
35            return out  
36      def all(seq):
37  class Model:      for x in seq:
38     """ the Model class provides a framework to run a time-dependent simulation. A Model has a set of parameter which          if not x:
39         may be fixed or altered by the Model itself or other Models over time.                return False
40        return True
41         The parameters of a models are declared at instantion, e.g.  
42    def any(seq):
43             m=Model({"message" : "none" })      for x in seq:
44            if x:
45         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:              return True
46        return False
         class Messenger(Model):  
             def __init__(self):  
                Model.__init__(self,parameters={"message" : "none" })  
48         There are various ways how model parameters can be changed:  LinkableObjectRegistry = {}
50         1) use object attributes:  def registerLinkableObject(obj_id, o):
51        LinkableObjectRegistry[obj_id] = o
53            m.message="Hello World!"  LinkRegistry = []
55         2) use setParamter method  def registerLink(obj_id, l):
56        LinkRegistry.append((obj_id,l))
58    def parse(xml):
59        """
60        Generic parse method for EsysXML. Without this, Links don't work.
61        """
62        global LinkRegistry, LinkableObjectRegistry
63        LinkRegistry = []
64        LinkableObjectRegistry = {}
66        doc = minidom.parseString(xml)
67        sim = getComponent(doc.firstChild)
68        for obj_id, link in LinkRegistry:
69            link.target = LinkableObjectRegistry[obj_id]
71        return sim
73    def getComponent(doc):
74        """
75        Used to get components of Simualtions, Models.
76        """
77        for node in doc.childNodes:
79                      if isinstance(node, minidom.Element):
80            m.setParameters(message="Hello World!")              if node.tagName == 'Simulation':
81                    if node.getAttribute("type") == 'Simulation':
82         3) or dictonaries                      return Simulation.fromDom(node)
83                  if node.tagName == 'Model':
84             d={ message : "Hello World!" }                  model_type = node.getAttribute("type")
85             m.setParameters(**d)                  model_subclasses = Model.__subclasses__()
86                    for model in model_subclasses:
87                        if model_type == model.__name__:
88         A model executed buy staring the run method of the model:                          return Model.fromDom(node)
89                if node.tagName == 'ParameterSet':
90            m=Messenger()                  parameter_type = node.getAttribute("type")
91            m.run()                  return ParameterSet.fromDom(node)
92                raise "Invalid simulation type, %r" % node.getAttribute("type")
        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)  
             def doFinalization(self):  
                print "I have no more to say!"  
        If a instance of the Messenger class is run, it will print the initialization and finalization message only.  
        This is because the default method for finalize() does always returns True and therefore the transition is  
        terminated startcht away.  
        Following example for solving the ODE using a forward euler scheme:  
95                  u(t=0)=u0      raise ValueError("No Simulation Found")
96                  u_t=a*u**2       for all 0<t<=ten              
98        exact solution is given by u(t)=1/(1/u0-a*t)  class Link:
99        """
100        a Link makes an attribute of an object callable:
101              o.object()
102              o.a=8
103              l=Link(o,"a")
104              assert l()==8
105         """
107        def __init__(self,target,attribute=None):
108            """
109            creates a link to the object target. If attribute is given, the link is
110            establised to this attribute of the target.  Otherwise the attribute is
111            undefined.
112            """
113            self.target = target
114            self.attribute = None
115            self.setAttributeName(attribute)
117        def setAttributeName(self,attribute):
118            """
119            set a new attribute name to be collected from the target object. The
120            target object must have the attribute with name attribute.
121            """
122            if attribute and self.target:
123                if isinstance(self.target,LinkableObject):
124                   if not self.target.hasAttribute(attribute):
125                      raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
126                else:
127                   if not hasattr(self.target,attribute):
128                      raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
129            self.attribute = attribute
131        def hasDefinedAttributeName(self):
132            """
133            returns true if an attribute name is set
134            """
135            return self.attribute != None
137        def __repr__(self):
138            """
139            returns a string representation of the link
140            """
141            if self.hasDefinedAttributeName():
142                return "<Link to attribute %s of %s>" % (self.attribute, self.target)
143            else:
144                return "<Link to target %s>" % self.target
146        def __call__(self,name=None):
147            """
148            returns the value of the attribute of the target object. If the
149            atrribute is callable then the return value of the call is returned.
150            """
151            if name:
152                out=getattr(self.target, name)
153            else:
154                out=getattr(self.target, self.attribute)
156            if callable(out):
157                return out()
158            else:
159                return out
161        def toDom(self, document, node):
162            """
163            toDom method of Link. Creates a Link node and appends it to the current XML
164            document
165            """
166            link = document.createElement('Link')
167            assert (self.target != None), ("Target was none, name was %r" % self.attribute)
168            link.appendChild(dataNode(document, 'Target', self.target.id))
169            # this use of id will not work for purposes of being able to retrieve the intended
170            # target from the xml later. I need a better unique identifier.
171            assert self.attribute, "You can't xmlify a Link without a target attribute"
172            link.appendChild(dataNode(document, 'Attribute', self.attribute))
173            node.appendChild(link)
175        def fromDom(cls, doc):
176            targetid = doc.getElementsByTagName("Target")[0].firstChild.nodeValue.strip()
177            attribute = doc.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip()
178            l = cls(None, attribute)
179            registerLink(targetid, l)
180            return l
182        fromDom = classmethod(fromDom)
184        def writeXML(self,ostream=stdout):
185            """
186            writes an XML representation of self to the output stream ostream.
187            If ostream is nor present the standart output stream is used.  If
188            esysheader==True the esys XML header is written
189            """
190            print 'I got to the Link writeXML method'
191            document, rootnode = esysDoc()
192            self.toDom(document, rootnode)
194            ostream.write(document.toprettyxml())
196    class LinkableObject(object):
197        """
198        An object that allows to link its attributes to attributes of other objects
199        via a Link object. For instance
201               p = LinkableObject()
202               p.x = Link(o,"name")
203               print p.x
205        links attribute x of p to the attribute name of object o.
207        p.x will contain the current value of attribute name of object o.  
209        If the value of getattr(o, "name") is callable, p.x will rturn the return
210        value of the call.
211        """
213        number_sequence = itertools.count(100)
215        def __init__(self, debug=False):
216            """ initializes LinkableObject so that we can operate on Links """
217            self.debug = debug
218            self.__linked_attributes={}
219            self.id = self.number_sequence.next()
221        def trace(self, msg):
222            """ If debugging is on, print the message, otherwise do nothing
223            """
224            if self.debug:
225                print "%s: %s"%(str(self),msg)
227        def __getattr__(self,name):
228            """returns the value of attribute name. If the value is a Link object the
229            object is called and the return value is returned."""
230            out = self.getAttributeObject(name)
231            if isinstance(out,Link):
232                return out()
233            else:
234                return out
236        def getAttributeObject(self,name):
237            """return the object stored for attribute name."""
239            if self.__dict__.has_key(name):
240                return self.__dict__[name]
242            if self.__linked_attributes.has_key(name):
243                return self.__linked_attributes[name]
245            if self.__class__.__dict__.has_key(name):
246                return self.__class.__dict__[name]
248            raise AttributeError,"No attribute %s."%name
250        def hasAttribute(self,name):
251            """returns True if self as attribute name"""
252            return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or  self.__class__.__dict__.has_key(name)
254        def __setattr__(self,name,value):
255            """sets the value for attribute name. If value is a Link the target
256            attribute is set to name if no attribute has been specified."""
259            if self.__dict__.has_key(name):
260                del self.__dict__[name]
262            if isinstance(value,Link):
263                if not value.hasDefinedAttributeName():
264                    value.setAttributeName(name)
265                self.__linked_attributes[name] = value
267                self.trace("attribute %s is now linked by %s."%(name,value))
268            else:
269                self.__dict__[name] = value
271        def __delattr__(self,name):
272            """removes the attribute name."""
274            if self.__linked_attributes.has_key[name]:
275                del self.__linked_attributes[name]
276            elif self.__dict__.has_key(name):
277                del self.__dict__[name]
278            else:
279                raise AttributeError,"No attribute %s."%name
281    class _ParameterIterator:
282        def __init__(self,parameterset):
284            self.__set=parameterset
285            self.__iter=iter(parameterset.parameters)
287        def next(self):
288            o=self.__iter.next()
289            return (o,self.__set.getAttributeObject(o))
291        class  Ode1(Model):      def __iter__(self):
292           def __init__(self,**args):          return self
             Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 1. },name="test",debug=True)  
294           def doInitialization(self):  class ParameterSet(LinkableObject):
295               self._tn=0      """a class which allows to emphazise attributes to be written and read to XML
297           Leaves of  an ESySParameters objects can be
299                a real number
300                a integer number
301                a string
302                a boolean value
303                a ParameterSet object
304                a Simulation object
305                a Model object
306                any other object (not considered by writeESySXML and writeXML)
308               Example how to create an ESySParameters object:
310                     p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.)
311                     p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11)
312                     parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1")))
314               This can be accessed as
316                     parm.parm1.gamma=0.
317                     parm.parm1.dim=2
318                     parm.parm1.tol_v=0.001
319                     parm.parm1.output_file="/tmp/u.%3.3d.dx"
320                     parm.parm1.runFlag=True
321                     parm.parm1.parm11.gamma1=1.
322                     parm.parm1.parm11.gamma2=2.
323                     parm.parm1.parm11.gamma3=3.
324                     parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1)
326        """
327        def __init__(self, parameters=[], **kwargs):
328            """creates a ParameterSet with parameters parameters"""
329            LinkableObject.__init__(self, **kwargs)
330            self.parameters = set()
331            self.declareParameters(parameters)
333        def __repr__(self):
334            return "<%s %r>" % (self.__class__.__name__,
335                                [(p, getattr(self, p, None)) for p in self.parameters])
337        def declareParameter(self,**parameters):
338            """declares a new parameter(s) and its (their) inital value."""
339            self.declareParameters(parameters)
341        def declareParameters(self,parameters):
342            """declares a set of parameters. parameters can be a list, a dictonary or a ParameterSet."""
343            if isinstance(parameters,ListType):
344                parameters = zip(parameters, itertools.repeat(None))
345            if isinstance(parameters,DictType):
346                parameters = parameters.iteritems()
348            for prm, value in parameters:
349                setattr(self,prm,value)
350                self.parameters.add(prm)
352                self.trace("parameter %s has been declared."%prm)
354        def releaseParameters(self,name):
355            """removes parameter name from the paramameters"""
356            if self.isParameter(name):
357                self.parameters.remove(name)
358                self.trace("parameter %s has been removed."%name)
360        def __iter__(self):
361            """creates an iterator over the parameter and their values"""
362            return _ParameterIterator(self)
364        def showParameters(self):
365            """returns a descrition of the parameters"""        
366            out="{"
367            notfirst=False
368            for i,v in self:
369                if notfirst: out=out+","
370                notfirst=True
371                if isinstance(v,ParameterSet):
372                    out="%s\"%s\" : %s"%(out,i,v.showParameters())
373                else:
374                    out="%s\"%s\" : %s"%(out,i,v)
375            return out+"}"
377        def __delattr__(self,name):
378            """removes the attribute name."""
379            LinkableObject.__delattr__(self,name)
380            try:
381                self.releaseParameter(name)
382            except:
383                pass
385        def toDom(self, document, node):
386            """ toDom method of ParameterSet class """
387            pset = document.createElement('ParameterSet')
388            node.appendChild(pset)
389            self._parametersToDom(document, pset)
391        def _parametersToDom(self, document, node):
392            node.setAttribute ('id', str(self.id))
393            for name,value in self:
394                param = document.createElement('Parameter')
395                param.setAttribute('type', value.__class__.__name__)
397                param.appendChild(dataNode(document, 'Name', name))
399                val = document.createElement('Value')
401                if isinstance(value,ParameterSet):
402                    value.toDom(document, val)
403                    param.appendChild(val)
404                elif isinstance(value, Link):
405                    value.toDom(document, val)
406                    param.appendChild(val)
407                elif isinstance(value,StringType):
408                    param.appendChild(dataNode(document, 'Value', value))
409                else:
410                    param.appendChild(dataNode(document, 'Value', str(value)))
412           def doStep(self,t):              node.appendChild(param)
414           def doFinalization(self):      def fromDom(cls, doc):
              print "all done final error = ",abs(self.u-1./(1./3.-self.a*self._tn))  
416           def getSafeTimeStepSize(self):          # Define a host of helper functions to assist us.
417               return self.dt          def _children(node):
418                """
419                Remove the empty nodes from the children of this node
420                """
421                return [x for x in node.childNodes
422                        if not isinstance(x, minidom.Text) or x.nodeValue.strip()]
424           def finalize(self):          def _floatfromValue(doc):
425               return self._tn>=self.tend              return float(doc.nodeValue.strip())
427         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(doc):
428         In this case the doStep() method is replaced by a sequance of methods which implements this iterative process.              return str(doc.nodeValue.strip())
429         The method then will control the iteration process by initializing the iteration through calling the        
430         doIterationInitialization() method. The iteration is preformed by calling the doIterationStep() method until          def _intfromValue(doc):
431         the terminate() method returns True. The doIterationFinalization() method is called to end the iteration.              return int(doc.nodeValue.strip())
        For a particular model these methods have to overwritten by a suitable subclass without touching the doStep() method.  
433         following example is a modification of the example above. Here an implicit euler scheme is used. in each time step the problem          def _boolfromValue(doc):
434                return bool(doc.nodeValue.strip())
436            # Mapping from text types in the xml to methods used to process trees of that type
437            ptypemap = {"Simulation": Simulation.fromDom,
438                        "Model":Model.fromDom,
439                        "ParameterSet":ParameterSet.fromDom,
440                        "Link":Link.fromDom,
441                        "float":_floatfromValue,
442                        "int":_intfromValue,
443                        "str":_stringfromValue,
444                        "bool":_boolfromValue
445                        }
447    #        print doc.toxml()
449            parameters = {}
450            for node in _children(doc):
451                ptype = node.getAttribute("type")
453                pname = pvalue = None
454                for childnode in _children(node):
456                    if childnode.tagName == "Name":
457                        pname = childnode.firstChild.nodeValue.strip()
459                    if childnode.tagName == "Value":
460                        nodes = _children(childnode)
461                        pvalue = ptypemap[ptype](nodes[0])
463                parameters[pname] = pvalue
465            # Create the instance of ParameterSet
466            o = cls()
467            o.declareParameters(parameters)
468            registerLinkableObject(doc.getAttribute("id"), o)
469            return o
471        fromDom = classmethod(fromDom)
473        def writeXML(self,ostream=stdout):
474            """writes the object as an XML object into an output stream"""
475            # ParameterSet(d) with d[Name]=Value
476            document, node = esysDoc()
477            self.toDom(document, node)
478            ostream.write(document.toprettyxml())
480    class Model(ParameterSet):
481        """
483        A Model object represents a processess marching over time
484        until a finalizing condition is fullfilled. At each time step an iterative
485        process can be performed and the time step size can be controlled. A Model has
486        the following work flow:
488              doInitialization()
489              while not finalize():
490                   dt=getSafeTimeStepSize(dt)
491                   doStepPreprocessing(dt)
492                   while not terminateIteration(): doStep(dt)
493                   doStepPostprocessing(dt)
494              doFinalization()
496              where doInitialization,finalize, getSafeTimeStepSize, doStepPreprocessing, terminateIteration, doStepPostprocessing, doFinalization
497              are methods of the particular instance of a Model. The default implementations of these methods have to be overwritten by
498              the subclass implementinf a Model.
500        """
502        UNDEF_DT=1.e300
504        def __init__(self,parameters=[],**kwarg):
505            """creates a model
507                Just calls the parent constructor.
508            """
509            ParameterSet.__init__(self, parameters=parameters,**kwarg)
511        def __str__(self):
512           return "<%s %d>"%(self.__class__,id(self))
514        def toDom(self, document, node):
515            """ toDom method of Model class """
516            pset = document.createElement('Model')
517            pset.setAttribute('type', self.__class__.__name__)
518            node.appendChild(pset)
519            self._parametersToDom(document, pset)
521        def doInitialization(self):
522            """initializes the time stepping scheme. This function may be overwritten."""
523            pass
525        def getSafeTimeStepSize(self,dt):
526            """returns a time step size which can safely be used.
527               dt gives the previously used step size.
528               This function may be overwritten."""
529            return self.UNDEF_DT
531        def finalize(self):
532            """returns False if the time stepping is finalized. This function may be
533            overwritten."""
534            return False
536        def doFinalization(self):
537            """finalizes the time stepping. This function may be overwritten."""
538            pass
540        def doStepPreprocessing(self,dt):
541            """sets up a time step of step size dt. This function may be overwritten."""
542            pass
544        def doStep(self,dt):
545            """executes an iteration step at a time step.
546               dt is the currently used time step size.
547               This function may be overwritten."""
548            pass
550        def terminateIteration(self):
551            """returns True if iteration on a time step is terminated."""
552            return True
554        def doStepPostprocessing(self,dt):
555            """finalalizes the time step.
556               dt is the currently used time step size.
557               This function may be overwritten."""
558            pass
560        def writeXML(self, ostream=stdout):
561            document, node = esysDoc()
562            self.toDom(document, node)
563            ostream.write(document.toprettyxml())
567    class Simulation(Model):
568        """
569              A Simulation object is special Model which runs a sequence of Models.
571              The methods doInitialization,finalize, getSafeTimeStepSize, doStepPreprocessing,
572              terminateIteration, doStepPostprocessing, doFinalization
573              are executing the corresponding methods of the models in the simulation.
575             0= u_{n+1}-u_{n}+a*dt*u_{n+1}**2      """
577         has to be solved for u_{n+1}. The Newton scheme is used to solve this non-linear problem.      FAILED_TIME_STEPS_MAX=20
578        MAX_ITER_STEPS=50
580        class  Ode2(Model):      def __init__(self, models=[], **kwargs):
581            """initiates a simulation from a list of models. """
582         def __init__(self,**args):          Model.__init__(self, **kwargs)
583             Model.__init__(self,{"tend" : 1., "dt" : 0.1 ,"a" : 10. ,"u" : 1. , "tol " : 1.e-8},"test","bla",None,True)          self.__models=[]
585         def doInitialization(self):          for i in range(len(models)):
586             self.__tn=0              self[i] = models[i]
588         def doIterationInitialization(self,t):      def __repr__(self):
589              self.__iter=0          """
590              self.u_last=self.u                      returns a string representation of the Simulation
591              self.current_dt=t-self.tn          """
592              self.__tn=t          return "<Simulation %r>" % self.__models
594         def doIterationStep(self):      def __str__(self):
595            self.__iter+=1          """
596            self.u_old=self.u          returning Simulation as a string
597            self.u=(self.current_dt*self.a*self.u**2-self.u_last)/(2*self.current_dt*self.a*self.u-1.)          """
598            return "<Simulation %d>"%id(self)
599         def terminate(self):      
600             return abs(self.u_old-self.u)<self.tol*abs(self.u)      def iterModels(self):
601            """returns an iterator over the models"""
602         def doIterationFinalization(self)          return self.__models
603             print "all done"      
604        def __getitem__(self,i):
605         def getSafeTimeStepSize(self):          """returns the i-th model"""
606             return self.dt          return self.__models[i]
608         def finalize(self):      def __setitem__(self,i,value):
609              return self.__tn>self.tend          """sets the i-th model"""
610            if not isinstance(value,Model):
611         A model can be composed from submodels. Submodels are treated as model parameters. If a model parameter is set or a value of              raise ValueError("assigned value is not a Model")
612         a model parameter is requested, the model will search for this parameter its submodels in the case the model does not have this          for j in range(max(i-len(self.__models)+1,0)): self.__models.append(None)
613         parameter itself. The order in which the submodels are searched is critical. By default a Model initializes all its submodels,          self.__models[i]=value
614         is finalized when all its submodels are finalized and finalizes all its submodels. In the case an iterative process is applied      
615         on a particular time step the iteration is initialized for all submodels, then the iteration step is performed for each submodel      def __len__(self):
616         until all submodels indicate termination. Then the iteration is finalized for all submodels. Finally teh doStop() method for all          """returns the number of models"""
617         submethods is called.          return len(self.__models)
619         Here we are creating a model which groups ab instantiation of the Ode2 and the Messenger Model      def toDom(self, document, node):
620            """ toDom method of Simulation class """
621         o=Ode2()          simulation = document.createElement('Simulation')
622         m=Messenger()          simulation.setAttribute('type', self.__class__.__name__)
623         om=Model(submodels=[o,m],debug=True)  
624         om.dt=0.01          for rank, sim in enumerate(self.iterModels()):
625         om.u=1.              component = document.createElement('Component')
626         m.message="it's me!"              component.setAttribute('rank', str(rank))
627         om.run()  
628                sim.toDom(document, component)
629         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  
630         automatically hand the assignment of new values down to the submodel. om.run() starts this combined model where now the soStep() method              simulation.appendChild(component)
631         of the Messenger object printing the value of its parameter message together with a time stamp is executed in each time step introduced  
632         by the Ode2 model.          node.appendChild(simulation)
634         A parameter of a Model can be linked to an attribute of onother object, typically an parameter of another Model object.          def writeXML(self,ostream=stdout):
635            """writes the object as an XML object into an output stream"""
636            document, rootnode = esysDoc()
637            self.toDom(document, rootnode)
638            ostream.write(document.toprettyxml())
640        def getSafeTimeStepSize(self,dt):
641            """returns a time step size which can safely be used by all models.
642               This is the minimum over the time step sizes of all models."""
643            out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
644            print "%s: safe step size is %e."%(str(self),out)
645            return out
647        def doInitialization(self):
648            """initializes all models """
649            self.n=0
650            self.tn=0.
651            for o in self.iterModels():
652                o.doInitialization()
654        def finalize(self):
655            """returns True if any of the models is to be finalized"""
656            return any([o.finalize() for o in self.iterModels()])
658        def doFinalization(self):
659            """finalalizes the time stepping for all models."""
660            for i in self.iterModels(): i.doFinalization()
661            self.trace("end of time integation.")
663        def doStepPreprocessing(self,dt):
664            """initializes the time step for all models."""
665            for o in self.iterModels():
666                o.doStepPreprocessing(dt)
668        def terminateIteration(self):
669            """returns True if all iterations for all models are terminated."""
670            out=all([o.terminateIteration() for o in self.iterModels()])
671            return out
673         which is comprised by a set of submodels.      def doStepPostprocessing(self,dt):
674         The simulation is run through its run method which in the simplest case has the form:          """finalalizes the iteration process for all models."""
675            for o in self.iterModels():
676            s=Model()              o.doStepPostprocessing(dt)
677            s.run()          self.n+=1
678            self.tn+=dt
679         The run has an initializion and finalization phase. The latter is called if all submodels are to be finalized. The      
680         simulation is processing in time through calling the stepForward methods which updates the observables of each submodel.      def doStep(self,dt):
681         A time steps size which is save for all submodel is choosen.          """
682                 executes the iteration step at a time step for all model:
683         At given time step an iterative process may be performed to make sure that all observables are consistent across all submodels.  
684         In this case, similar the time dependence, an initialization and finalization of the iteration is performed.                    self.doStepPreprocessing(dt)
685                      while not self.terminateIteration(): for all models: self.doStep(dt)
686         A Model has input and output parameters where each input parameter can be constant, time dependent or may depend on an                    self.doStepPostprocessing(dt)
687         output parameter of another model or the model itself. To create a parameter name of a model and to  
688         assign a value to it one can use the statement          """
689            self.iter=0
690             model.name=object          while not self.terminateIteration():
691                if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
692                self.iter+=1
693                self.trace("iteration step %d"%(self.iter))
694                for o in self.iterModels():
695                      o.doStep(dt)
696            if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
698        def run(self,check_point=None):
699            """
701               run the simulation by performing essentially
703                   self.doInitialization()
704                   while not self.finalize():
705                      dt=self.getSafeTimeStepSize()
706                      self.doStep(dt)
707                      if n%check_point==0: self.writeXML()
708                   self.doFinalization()
710         At any time the current value of the parameter name can be obtained by             If one of the models in throws a FailedTimeStepError exception a new time step size is
711               computed through getSafeTimeStepSize() and the time step is repeated.
713               If one of the models in throws a IterationDivergenceError exception the time step size
714               is halved and the time step is repeated.
716                 value=model.name             In both cases the time integration is given up after Simulation.FAILED_TIME_STEPS_MAX
717               attempts.
719         If the object that has been assigned to the paramter/attribute name has the attribute/parameter name isself the current value of this      
720         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          """
721         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          dt=self.UNDEF_DT
722            self.doInitialization()
723            while not self.finalize():
724                step_fail_counter=0
725                iteration_fail_counter=0
726                dt_new=self.getSafeTimeStepSize(dt)
727                self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
728                end_of_step=False
729                while not end_of_step:
730                   end_of_step=True
731                   if not dt_new>0:
732                      raise NonPositiveStepSizeError("non-positive step size in step %d",self.n+1)
733                   try:
734                      self.doStepPreprocessing(dt_new)
735                      self.doStep(dt_new)
736                      self.doStepPostprocessing(dt_new)
737                   except IterationDivergenceError:
738                      dt_new*=0.5
739                      end_of_step=False
740                      iteration_fail_counter+=1
741                      if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
742                               raise SimulationBreakDownError("reduction of time step to achieve convergence failed.")
743                      self.trace("iteration fails. time step is repeated with new step size.")
744                   except FailedTimeStepError:
745                      dt_new=self.getSafeTimeStepSize(dt)
746                      end_of_step=False
747                      step_fail_counter+=1
748                      self.trace("time step is repeated.")
749                      if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
750                            raise SimulationBreakDownError("time integration is given up after %d attempts."%step_fail_counter)
751                dt=dt_new
752                if not check_point==None:
753                    if n%check_point==0:
754                        self.trace("check point is created.")
755                        self.writeXML()
756            self.doFinalization()
758        def fromDom(cls, doc):
759            sims = []
760            for node in doc.childNodes:
761                if isinstance(node, minidom.Text):
762                    continue
764             model.setParameter(name,object,name_for_object)              sims.append(getComponent(node))
766         links the parameter name of model with the parameter name_for_object of object.          return cls(sims)
768         The run method initiates checkpointing (it is not clear how to do this yet)      fromDom = classmethod(fromDom)
    # step size used in case of an undefined value for the step size  
    def __init__(self,submodels=[],parameters={},name="model",description="none",check_pointing=None,debug=False):  
       """initiates a model from a list of submodels. """  
       # get the models defined in parameters:  
       # submodels==None means no submodels used:  
       if submodels==None:  
       # no submodel list given means all submodels are used as defined by the parameters dictionary:  
       elif len(submodels)==0:  
             for i in parameters.keys():  
                 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.  
          for i in submodels:  
             if isinstance(i,StringType):  
               if not isinstance(m,Model):  
                  raise ValueError,"submodel %s is not a model."%i  
                if not isinstance(i,Model):  
                  raise ValueError,"submodel list does contain item which is not a Model class object."  
                self.declareParameter(**{i : m})  
             if self.debug(): print "%s: model %s is added as parameter %s."%(self,m,i)  
       if len(self.__submodels)>0 and self.debug(): print "%s: model ordering is %s"%(self,self.__submodels)  
    def setSubmodelOrder(submodels=[]):  
       """sets a new ordering for submodels"""  
    # some basic fuctions:  
    def debugOn(self):  
       """sets debugging to on"""  
    def debugOff(self):  
       """sets debugging to off"""  
    def debug(self):  
       """returns True if debug mode is set to on"""  
       return self.__debug  
    def setDebug(self,flag=False):  
       """sets debugging to flag"""  
       if flag:  
    def setDebug(self,flag=False):  
       """sets debugging to flag"""  
       if flag:  
    # name and description handling  
    def __str__(self):  
        """returns the name of the model"""  
        return self.getName()  
    def getName(self):  
        """returns the name of the model"""  
        return self.__name  
    def getFullName(self):  
        """returns the full name of the model including all the names of the submodels"""  
        for i in self.__submodels:  
             if notfirst: out=out+","  
        return out+")"  
    def setName(self,name):  
        """sets the name of the model"""  
    def setDescription(self,description="none"):  
        """sets new description"""  
        if self.debug(): print "%s: description is set to %s."%(self,description)  
    def getDescription(self):  
        """returns the description of the model"""  
        return self.__description  
    #    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)  
          if self.debug(): print "%s: parameter %s has been declared."%(self,prm)  
    def showParameters(self):  
       """returns a descrition of the parameters"""  
       for i in self.__parameters:  
           if notfirst: out=out+","  
       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):  
           for i in self.__submodels:  
              except IllegalParameterError:  
           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)  
             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])  
             for i in self.__submodels:  
                    self.__parameters[i].setParameter(**{name : parameters[name]})  
                 except IllegalParameterError:  
             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):  
          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)  
          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 })  
    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"):  
          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."""  
       for i in self.__submodels:  
           if not dt==None:  
               if out==None:  
       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"""  
       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."""  
       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"""  
    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:  
       while not self.terminate(): self.doIterationStep()  
    def finalize(self):  
       """returns True if all submodels are to be finalized"""  
       return self.finalizeAllSubmodels()  
    def doFinalization(self):  
       """finalizes the time stepping."""  
    #   methods deal with iterations:  
    def doIterationInitialization(self,t):  
       """initializes the iteration on a time step"""  
       if self.debug(): print "%s: iteration starts"%self  
    def doIterationStep(self):  
       """executes the iteration step"""  
       if self.debug(): print "%s: iteration step %d"%(self,self.__iter)  
       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."""  
       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"""  
       while not self.finalize():  
          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)  
          while not endoftimestep:  
               except FailedTimeStepError:  
                  if self.__dt==None: self.__dt=self.UNDEF_DT  
                  if self.debug(): print "%s: time step is repeated with new step size %e."%(self,self.__dt)  
               except IterationDivergenceError:  
                  if self.debug(): print "%s: iteration failes. time step is repeated with new step size %e."%(self,self.__dt)  
771  class IterationDivergenceError(Exception):  class IterationDivergenceError(Exception):
772      """excpetion which should be thrown if an iteration at a time step fails"""      """
773           excpetion which is thrown if there is no convergence of the iteration process at a time step
774           but there is a chance taht a smaller step could help to reach convergence.
776        """
777      pass      pass
779  class FailedTimeStepError(Exception):  class FailedTimeStepError(Exception):
780      """excpetion which should be thrown if the time step fails because of a step size that have been choosen to be to large"""      """excpetion which is thrown if the time step fails because of a step size that have been choosen to be too large"""
781      pass      pass
783  class IllegalParameterError(Exception):  class SimulationBreakDownError(Exception):
784      """excpetion which is thrown if model has not the desired parameter"""      """excpetion which is thrown if the simulation does not manage to progress in time."""
785      pass      pass
787    class NonPositiveStepSizeError(Exception):
788  if __name__=="__main__":      """excpetion which is thrown if the step size is not positive"""
789     class Messenger(Model):      pass
       def __init__(self):  
          Model.__init__(self,parameters={"message" : "none" },name="messenger")  
       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):  
       def doStep(self,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)  
        def doInitialization(self):  
        def doIterationInitialization(self,t):  
        def doIterationStep(self):  
        def terminate(self):  
           if self._iter<1:  
               return False  
              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)  
        def doInitialization(self):  
        def doStep(self,t):  
            print "test1 set the value out1 to ",self.p3  
        def doFinalization(self):  
        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  
    class Test12(Model):  
      """model build from two models in a transperent way"""  
      def __init__(self):  
          Model.__init__(self,{"sm1": None, a : 0, "sm2": None},"test2","",None,True)  
    # test messenger  
    # ode1        
    # ode1  
    # and they are linked together:  
    print om.showParameters()  
    # and a coupled problem:  

Removed from v.121  
changed lines
  Added in v.147

  ViewVC Help
Powered by ViewVC 1.1.26