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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 906 - (hide annotations)
Tue Nov 21 05:34:15 2006 UTC (12 years, 5 months ago) by gross
File MIME type: text/x-python
File size: 34388 byte(s)
some minor fixes
1 jgs 121 # $Id$
2    
3 gross 637 """
4     Environment for implementing models in escript
5    
6     @var __author__: name of author
7     @var __copyright__: copyrights
8     @var __license__: licence agreement
9     @var __url__: url entry point on documentation
10     @var __version__: version
11     @var __date__: date of the version
12     """
13    
14     __author__="Lutz Gross, l.gross@uq.edu.au"
15 elspeth 609 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
16     http://www.access.edu.au
17     Primary Business: Queensland, Australia"""
18 elspeth 614 __license__="""Licensed under the Open Software License version 3.0
19     http://www.opensource.org/licenses/osl-3.0.php"""
20 gross 637 __url__="http://www.iservo.edu.au/esys"
21     __version__="$Revision$"
22     __date__="$Date$"
23 elspeth 609
24 gross 637
25 jgs 122 from types import StringType,IntType,FloatType,BooleanType,ListType,DictType
26     from sys import stdout
27 elspeth 871 import numarray
28     import operator
29 jgs 123 import itertools
30 jgs 142 # import modellib temporarily removed!!!
31 jgs 121
32 jgs 123 # import the 'set' module if it's not defined (python2.3/2.4 difference)
33     try:
34     set
35     except NameError:
36     from sets import Set as set
37    
38 jgs 122 from xml.dom import minidom
39 jgs 121
40 jgs 122 def dataNode(document, tagName, data):
41 jgs 126 """
42 jgs 149 C{dataNode}s are the building blocks of the xml documents constructed in
43     this module.
44    
45     @param document: the current xml document
46     @param tagName: the associated xml tag
47     @param data: the values in the tag
48 jgs 126 """
49 jgs 123 t = document.createTextNode(str(data))
50     n = document.createElement(tagName)
51     n.appendChild(t)
52     return n
53 jgs 121
54 jgs 122 def esysDoc():
55 jgs 126 """
56     Global method for creating an instance of an EsysXML document.
57     """
58 jgs 123 doc = minidom.Document()
59     esys = doc.createElement('ESys')
60     doc.appendChild(esys)
61     return doc, esys
62 jgs 121
63 jgs 123 def all(seq):
64     for x in seq:
65     if not x:
66     return False
67     return True
68    
69 jgs 147 def any(seq):
70     for x in seq:
71     if x:
72     return True
73     return False
74    
75 jgs 123 LinkableObjectRegistry = {}
76    
77     def registerLinkableObject(obj_id, o):
78     LinkableObjectRegistry[obj_id] = o
79    
80     LinkRegistry = []
81    
82     def registerLink(obj_id, l):
83     LinkRegistry.append((obj_id,l))
84    
85     def parse(xml):
86 jgs 147 """
87 jgs 149 Generic parse method for EsysXML. Without this, Links don't work.
88 jgs 147 """
89 jgs 123 global LinkRegistry, LinkableObjectRegistry
90     LinkRegistry = []
91     LinkableObjectRegistry = {}
92    
93     doc = minidom.parseString(xml)
94     sim = getComponent(doc.firstChild)
95     for obj_id, link in LinkRegistry:
96     link.target = LinkableObjectRegistry[obj_id]
97    
98     return sim
99    
100 jgs 150 def importName(modulename, name):
101     """ Import a named object from a module in the context of this function,
102     which means you should use fully qualified module paths.
103    
104     Return None on failure.
105    
106     This function from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52241
107     """
108     module = __import__(modulename, globals(), locals(), [name])
109    
110     try:
111     return vars(module)[name]
112     except KeyError:
113     raise ImportError("Could not import %s from %s" % (name, modulename))
114    
115 jgs 123 def getComponent(doc):
116 jgs 147 """
117     Used to get components of Simualtions, Models.
118     """
119 jgs 123 for node in doc.childNodes:
120 jgs 147
121 jgs 123 if isinstance(node, minidom.Element):
122     if node.tagName == 'Simulation':
123     if node.getAttribute("type") == 'Simulation':
124     return Simulation.fromDom(node)
125     if node.tagName == 'Model':
126 jgs 150 if (node.getAttribute("module")):
127     model_module = node.getAttribute("module")
128     model_type = node.getAttribute("type")
129     return importName(model_module, model_type).fromDom(node)
130     else:
131     model_type = node.getAttribute("type")
132     model_subclasses = Model.__subclasses__()
133     for model in model_subclasses:
134     if model_type == model.__name__:
135     return Model.fromDom(node)
136 jgs 147 if node.tagName == 'ParameterSet':
137     parameter_type = node.getAttribute("type")
138     return ParameterSet.fromDom(node)
139 jgs 123 raise "Invalid simulation type, %r" % node.getAttribute("type")
140 jgs 147
141 jgs 123
142     raise ValueError("No Simulation Found")
143    
144    
145 jgs 122 class Link:
146 jgs 123 """
147 jgs 149 A Link makes an attribute of an object callable::
148    
149 jgs 123 o.object()
150     o.a=8
151     l=Link(o,"a")
152     assert l()==8
153     """
154    
155     def __init__(self,target,attribute=None):
156 jgs 126 """
157 jgs 149 Creates a link to the object target. If attribute is given, the link is
158 jgs 123 establised to this attribute of the target. Otherwise the attribute is
159 jgs 126 undefined.
160     """
161 jgs 123 self.target = target
162     self.attribute = None
163     self.setAttributeName(attribute)
164    
165     def setAttributeName(self,attribute):
166 jgs 126 """
167 jgs 149 Set a new attribute name to be collected from the target object. The
168 jgs 126 target object must have the attribute with name attribute.
169     """
170 jgs 147 if attribute and self.target:
171     if isinstance(self.target,LinkableObject):
172     if not self.target.hasAttribute(attribute):
173     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
174     else:
175     if not hasattr(self.target,attribute):
176     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
177 jgs 123 self.attribute = attribute
178    
179     def hasDefinedAttributeName(self):
180 jgs 126 """
181 jgs 149 Returns true if an attribute name is set.
182 jgs 126 """
183 jgs 123 return self.attribute != None
184    
185     def __repr__(self):
186 jgs 126 """
187 jgs 149 Returns a string representation of the link.
188 jgs 126 """
189 jgs 123 if self.hasDefinedAttributeName():
190     return "<Link to attribute %s of %s>" % (self.attribute, self.target)
191     else:
192     return "<Link to target %s>" % self.target
193    
194     def __call__(self,name=None):
195 jgs 126 """
196 jgs 149 Returns the value of the attribute of the target object. If the
197 jgs 126 atrribute is callable then the return value of the call is returned.
198     """
199 jgs 123 if name:
200     out=getattr(self.target, name)
201     else:
202     out=getattr(self.target, self.attribute)
203 jgs 121
204 jgs 123 if callable(out):
205     return out()
206     else:
207     return out
208 jgs 121
209 jgs 123 def toDom(self, document, node):
210 jgs 126 """
211 jgs 149 C{toDom} method of Link. Creates a Link node and appends it to the
212     current XML document.
213 jgs 126 """
214 jgs 123 link = document.createElement('Link')
215 jgs 147 assert (self.target != None), ("Target was none, name was %r" % self.attribute)
216 jgs 123 link.appendChild(dataNode(document, 'Target', self.target.id))
217     # this use of id will not work for purposes of being able to retrieve the intended
218     # target from the xml later. I need a better unique identifier.
219     assert self.attribute, "You can't xmlify a Link without a target attribute"
220     link.appendChild(dataNode(document, 'Attribute', self.attribute))
221     node.appendChild(link)
222 jgs 121
223 jgs 123 def fromDom(cls, doc):
224     targetid = doc.getElementsByTagName("Target")[0].firstChild.nodeValue.strip()
225     attribute = doc.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip()
226     l = cls(None, attribute)
227     registerLink(targetid, l)
228     return l
229 jgs 121
230 jgs 123 fromDom = classmethod(fromDom)
231    
232     def writeXML(self,ostream=stdout):
233 jgs 126 """
234 jgs 149 Writes an XML representation of self to the output stream ostream.
235 jgs 123 If ostream is nor present the standart output stream is used. If
236 jgs 126 esysheader==True the esys XML header is written
237     """
238 jgs 147 print 'I got to the Link writeXML method'
239 jgs 123 document, rootnode = esysDoc()
240     self.toDom(document, rootnode)
241    
242     ostream.write(document.toprettyxml())
243    
244 jgs 122 class LinkableObject(object):
245 jgs 123 """
246     An object that allows to link its attributes to attributes of other objects
247 jgs 149 via a Link object. For instance::
248 jgs 123
249     p = LinkableObject()
250     p.x = Link(o,"name")
251     print p.x
252    
253 jgs 149 links attribute C{x} of C{p} to the attribute name of object C{o}.
254 jgs 121
255 jgs 149 C{p.x} will contain the current value of attribute C{name} of object
256     C{o}.
257 jgs 121
258 jgs 149 If the value of C{getattr(o, "name")} is callable, C{p.x} will return
259     the return value of the call.
260 jgs 123 """
261    
262     number_sequence = itertools.count(100)
263    
264     def __init__(self, debug=False):
265 jgs 149 """
266     Initializes LinkableObject so that we can operate on Links
267     """
268 jgs 123 self.debug = debug
269     self.__linked_attributes={}
270     self.id = self.number_sequence.next()
271 jgs 149 registerLinkableObject(self.id, self)
272 jgs 123
273     def trace(self, msg):
274     """
275 jgs 149 If debugging is on, print the message, otherwise do nothing
276     """
277 jgs 123 if self.debug:
278 jgs 147 print "%s: %s"%(str(self),msg)
279 jgs 123
280     def __getattr__(self,name):
281 jgs 149 """
282     Returns the value of attribute name. If the value is a Link object the
283     object is called and the return value is returned.
284     """
285 jgs 123 out = self.getAttributeObject(name)
286     if isinstance(out,Link):
287     return out()
288     else:
289     return out
290    
291     def getAttributeObject(self,name):
292 jgs 149 """
293     Return the object stored for attribute name.
294     """
295 jgs 123
296     if self.__dict__.has_key(name):
297     return self.__dict__[name]
298    
299     if self.__linked_attributes.has_key(name):
300     return self.__linked_attributes[name]
301    
302 jgs 147 if self.__class__.__dict__.has_key(name):
303     return self.__class.__dict__[name]
304    
305 jgs 123 raise AttributeError,"No attribute %s."%name
306    
307 jgs 147 def hasAttribute(self,name):
308 jgs 149 """
309     Returns True if self as attribute name.
310     """
311 jgs 147 return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name)
312    
313 jgs 123 def __setattr__(self,name,value):
314 jgs 149 """
315     Sets the value for attribute name. If value is a Link the target
316     attribute is set to name if no attribute has been specified.
317     """
318 jgs 123
319     if self.__dict__.has_key(name):
320     del self.__dict__[name]
321    
322     if isinstance(value,Link):
323     if not value.hasDefinedAttributeName():
324     value.setAttributeName(name)
325     self.__linked_attributes[name] = value
326    
327 jgs 147 self.trace("attribute %s is now linked by %s."%(name,value))
328 jgs 123 else:
329     self.__dict__[name] = value
330    
331     def __delattr__(self,name):
332 jgs 149 """
333     Removes the attribute name.
334     """
335 jgs 123
336     if self.__linked_attributes.has_key[name]:
337     del self.__linked_attributes[name]
338     elif self.__dict__.has_key(name):
339     del self.__dict__[name]
340     else:
341     raise AttributeError,"No attribute %s."%name
342    
343 jgs 122 class _ParameterIterator:
344 jgs 123 def __init__(self,parameterset):
345 jgs 121
346 jgs 123 self.__set=parameterset
347     self.__iter=iter(parameterset.parameters)
348    
349     def next(self):
350     o=self.__iter.next()
351     return (o,self.__set.getAttributeObject(o))
352    
353     def __iter__(self):
354     return self
355    
356 jgs 122 class ParameterSet(LinkableObject):
357 jgs 149 """
358     A class which allows to emphazise attributes to be written and read to XML
359 jgs 123
360 jgs 149 Leaves of an ESySParameters object can be:
361 jgs 123
362 jgs 149 - a real number
363     - a integer number
364     - a string
365     - a boolean value
366     - a ParameterSet object
367     - a Simulation object
368     - a Model object
369 elspeth 874 - a numarray object
370     - a list of booleans
371     - any other object (not considered by writeESySXML and writeXML)
372 jgs 123
373 jgs 149 Example how to create an ESySParameters object::
374 jgs 123
375 jgs 149 p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.)
376     p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11)
377     parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1")))
378 jgs 123
379 jgs 149 This can be accessed as::
380 jgs 123
381 jgs 149 parm.parm1.gamma=0.
382     parm.parm1.dim=2
383     parm.parm1.tol_v=0.001
384     parm.parm1.output_file="/tmp/u.%3.3d.dx"
385     parm.parm1.runFlag=True
386     parm.parm1.parm11.gamma1=1.
387     parm.parm1.parm11.gamma2=2.
388     parm.parm1.parm11.gamma3=3.
389     parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1)
390 jgs 123 """
391     def __init__(self, parameters=[], **kwargs):
392 jgs 149 """
393     Creates a ParameterSet with parameters parameters.
394     """
395 jgs 123 LinkableObject.__init__(self, **kwargs)
396     self.parameters = set()
397     self.declareParameters(parameters)
398 jgs 147
399     def __repr__(self):
400     return "<%s %r>" % (self.__class__.__name__,
401     [(p, getattr(self, p, None)) for p in self.parameters])
402 jgs 123
403     def declareParameter(self,**parameters):
404 jgs 149 """
405     Declares a new parameter(s) and its (their) initial value.
406     """
407 jgs 123 self.declareParameters(parameters)
408    
409     def declareParameters(self,parameters):
410 jgs 149 """
411     Declares a set of parameters. parameters can be a list, a dictionary
412     or a ParameterSet.
413     """
414 jgs 123 if isinstance(parameters,ListType):
415     parameters = zip(parameters, itertools.repeat(None))
416     if isinstance(parameters,DictType):
417     parameters = parameters.iteritems()
418 jgs 121
419 jgs 123 for prm, value in parameters:
420     setattr(self,prm,value)
421     self.parameters.add(prm)
422 jgs 121
423 jgs 147 self.trace("parameter %s has been declared."%prm)
424 jgs 121
425 jgs 123 def releaseParameters(self,name):
426 jgs 149 """
427     Removes parameter name from the paramameters.
428     """
429 jgs 123 if self.isParameter(name):
430     self.parameters.remove(name)
431 jgs 147 self.trace("parameter %s has been removed."%name)
432 jgs 123
433     def __iter__(self):
434 jgs 149 """
435     Creates an iterator over the parameter and their values.
436     """
437 jgs 123 return _ParameterIterator(self)
438    
439     def showParameters(self):
440 jgs 149 """
441     Returns a descrition of the parameters.
442     """
443 jgs 123 out="{"
444     notfirst=False
445     for i,v in self:
446     if notfirst: out=out+","
447     notfirst=True
448     if isinstance(v,ParameterSet):
449     out="%s\"%s\" : %s"%(out,i,v.showParameters())
450     else:
451     out="%s\"%s\" : %s"%(out,i,v)
452     return out+"}"
453    
454     def __delattr__(self,name):
455 jgs 149 """
456     Removes the attribute name.
457     """
458 jgs 123 LinkableObject.__delattr__(self,name)
459     try:
460     self.releaseParameter(name)
461     except:
462     pass
463 jgs 121
464 jgs 123 def toDom(self, document, node):
465 jgs 149 """
466     C{toDom} method of ParameterSet class.
467     """
468 jgs 123 pset = document.createElement('ParameterSet')
469     node.appendChild(pset)
470     self._parametersToDom(document, pset)
471 jgs 121
472 jgs 123 def _parametersToDom(self, document, node):
473 elspeth 282 node.setAttribute('id', str(self.id))
474     node.setIdAttribute("id")
475 jgs 123 for name,value in self:
476     param = document.createElement('Parameter')
477     param.setAttribute('type', value.__class__.__name__)
478 jgs 121
479 jgs 123 param.appendChild(dataNode(document, 'Name', name))
480 jgs 121
481 jgs 123 val = document.createElement('Value')
482    
483 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
484 jgs 123 value.toDom(document, val)
485     param.appendChild(val)
486 elspeth 871 elif isinstance(value, numarray.NumArray):
487     shape = value.getshape()
488     if isinstance(shape, tuple):
489     size = reduce(operator.mul, shape)
490     shape = ' '.join(map(str, shape))
491     else:
492     size = shape
493     shape = str(shape)
494    
495     arraytype = value.type()
496 elspeth 874 numarrayElement = document.createElement('NumArray')
497     numarrayElement.appendChild(dataNode(document, 'ArrayType', str(arraytype)))
498     numarrayElement.appendChild(dataNode(document, 'Shape', shape))
499     numarrayElement.appendChild(dataNode(document, 'Data', ' '.join(
500 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
501 elspeth 874 val.appendChild(numarrayElement)
502 elspeth 871 param.appendChild(val)
503 elspeth 874 elif isinstance (value, list):
504     param.appendChild(dataNode(document, 'Value', ' '.join(
505     [str(x) for x in value])
506     ))
507 jgs 123 else:
508     param.appendChild(dataNode(document, 'Value', str(value)))
509    
510     node.appendChild(param)
511    
512     def fromDom(cls, doc):
513    
514     # Define a host of helper functions to assist us.
515     def _children(node):
516     """
517 jgs 149 Remove the empty nodes from the children of this node.
518 jgs 123 """
519 elspeth 871 ret = []
520     for x in node.childNodes:
521     if isinstance(x, minidom.Text):
522     if x.nodeValue.strip():
523     ret.append(x)
524     else:
525     ret.append(x)
526     return ret
527 jgs 123
528     def _floatfromValue(doc):
529     return float(doc.nodeValue.strip())
530    
531     def _stringfromValue(doc):
532     return str(doc.nodeValue.strip())
533 jgs 126
534     def _intfromValue(doc):
535     return int(doc.nodeValue.strip())
536    
537     def _boolfromValue(doc):
538 elspeth 874 return _boolfromstring(doc.nodeValue.strip())
539 elspeth 266
540     def _nonefromValue(doc):
541     return None
542 elspeth 871
543     def _numarrayfromValue(doc):
544 elspeth 874 for node in _children(doc):
545 elspeth 871 if node.tagName == 'ArrayType':
546     arraytype = node.firstChild.nodeValue.strip()
547     if node.tagName == 'Shape':
548     shape = node.firstChild.nodeValue.strip()
549     shape = [int(x) for x in shape.split()]
550     if node.tagName == 'Data':
551     data = node.firstChild.nodeValue.strip()
552     data = [float(x) for x in data.split()]
553     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
554     shape)
555 elspeth 874
556     def _listfromValue(doc):
557     return [_boolfromstring(x) for x in doc.nodeValue.split()]
558    
559    
560     def _boolfromstring(s):
561     if s == 'True':
562     return True
563     else:
564     return False
565 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
566     ptypemap = {"Simulation": Simulation.fromDom,
567     "Model":Model.fromDom,
568     "ParameterSet":ParameterSet.fromDom,
569     "Link":Link.fromDom,
570 elspeth 875 "DataSource":DataSource.fromDom,
571 jgs 123 "float":_floatfromValue,
572 jgs 126 "int":_intfromValue,
573 jgs 123 "str":_stringfromValue,
574 elspeth 269 "bool":_boolfromValue,
575 elspeth 874 "list":_listfromValue,
576     "NumArray":_numarrayfromValue,
577 elspeth 871 "NoneType":_nonefromValue,
578 jgs 123 }
579    
580 jgs 147 # print doc.toxml()
581    
582 jgs 123 parameters = {}
583     for node in _children(doc):
584     ptype = node.getAttribute("type")
585    
586     pname = pvalue = None
587     for childnode in _children(node):
588    
589     if childnode.tagName == "Name":
590     pname = childnode.firstChild.nodeValue.strip()
591    
592     if childnode.tagName == "Value":
593     nodes = _children(childnode)
594 elspeth 874 # if ptype == 'NumArray':
595     # pvalue = _numarrayfromValue(nodes)
596     # else:
597     pvalue = ptypemap[ptype](nodes[0])
598 jgs 123
599     parameters[pname] = pvalue
600    
601     # Create the instance of ParameterSet
602     o = cls()
603     o.declareParameters(parameters)
604     registerLinkableObject(doc.getAttribute("id"), o)
605     return o
606    
607     fromDom = classmethod(fromDom)
608    
609     def writeXML(self,ostream=stdout):
610 jgs 149 """
611     Writes the object as an XML object into an output stream.
612     """
613 jgs 123 # ParameterSet(d) with d[Name]=Value
614     document, node = esysDoc()
615     self.toDom(document, node)
616     ostream.write(document.toprettyxml())
617    
618 jgs 147 class Model(ParameterSet):
619     """
620 jgs 149 A Model object represents a processess marching over time until a
621     finalizing condition is fullfilled. At each time step an iterative
622     process can be performed and the time step size can be controlled. A
623     Model has the following work flow::
624 gross 906
625 jgs 147 doInitialization()
626 gross 906 while not terminateInitialIteration(): doInitializationiStep()
627     doInitialPostprocessing()
628 jgs 147 while not finalize():
629     dt=getSafeTimeStepSize(dt)
630     doStepPreprocessing(dt)
631     while not terminateIteration(): doStep(dt)
632     doStepPostprocessing(dt)
633     doFinalization()
634 jgs 121
635 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
636     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
637     C{doFinalization} are methods of the particular instance of a Model. The
638     default implementations of these methods have to be overwritten by the
639     subclass implementing a Model.
640 jgs 147 """
641 jgs 121
642 jgs 147 UNDEF_DT=1.e300
643 jgs 121
644 jgs 147 def __init__(self,parameters=[],**kwarg):
645 jgs 149 """
646     Creates a model.
647 jgs 121
648 jgs 149 Just calls the parent constructor.
649 jgs 147 """
650     ParameterSet.__init__(self, parameters=parameters,**kwarg)
651 jgs 121
652 jgs 147 def __str__(self):
653     return "<%s %d>"%(self.__class__,id(self))
654 jgs 121
655 jgs 147 def toDom(self, document, node):
656 jgs 149 """
657     C{toDom} method of Model class
658     """
659 jgs 147 pset = document.createElement('Model')
660     pset.setAttribute('type', self.__class__.__name__)
661 jgs 150 if not self.__class__.__module__.startswith('esys.escript'):
662     pset.setAttribute('module', self.__class__.__module__)
663 jgs 147 node.appendChild(pset)
664     self._parametersToDom(document, pset)
665 jgs 121
666 jgs 147 def doInitialization(self):
667 jgs 149 """
668     Initializes the time stepping scheme.
669    
670     This function may be overwritten.
671     """
672 jgs 147 pass
673 gross 906 def doInitialStep(self):
674     """
675     performs an iteration step in the initialization phase
676    
677     This function may be overwritten.
678     """
679     pass
680    
681     def terminateInitialIteration(self):
682     """
683     Returns True if iteration at the inital phase is terminated.
684     """
685     return True
686    
687     def doInitialPostprocessing(self):
688     """
689     finalises the initialization iteration process
690    
691     This function may be overwritten.
692     """
693     pass
694 jgs 147
695     def getSafeTimeStepSize(self,dt):
696 jgs 149 """
697     Returns a time step size which can safely be used.
698    
699     C{dt} gives the previously used step size.
700    
701     This function may be overwritten.
702     """
703 jgs 147 return self.UNDEF_DT
704    
705     def finalize(self):
706 jgs 149 """
707     Returns False if the time stepping is finalized.
708    
709     This function may be overwritten.
710     """
711 jgs 147 return False
712 jgs 123
713 jgs 147 def doFinalization(self):
714 jgs 149 """
715     Finalizes the time stepping.
716    
717     This function may be overwritten.
718     """
719 jgs 147 pass
720    
721     def doStepPreprocessing(self,dt):
722 jgs 149 """
723     Sets up a time step of step size dt.
724    
725     This function may be overwritten.
726     """
727 jgs 147 pass
728    
729     def doStep(self,dt):
730 jgs 149 """
731     Executes an iteration step at a time step.
732    
733     C{dt} is the currently used time step size.
734    
735     This function may be overwritten.
736     """
737 jgs 147 pass
738    
739     def terminateIteration(self):
740 jgs 149 """
741     Returns True if iteration on a time step is terminated.
742     """
743 jgs 147 return True
744 gross 906
745 jgs 147
746     def doStepPostprocessing(self,dt):
747 jgs 149 """
748 gross 906 finalises the time step.
749 jgs 149
750     dt is the currently used time step size.
751    
752     This function may be overwritten.
753     """
754 jgs 147 pass
755    
756     def writeXML(self, ostream=stdout):
757     document, node = esysDoc()
758     self.toDom(document, node)
759     ostream.write(document.toprettyxml())
760    
761 jgs 121
762 jgs 147 class Simulation(Model):
763     """
764 jgs 149 A Simulation object is special Model which runs a sequence of Models.
765 jgs 121
766 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
767     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
768     C{doFinalization} are executing the corresponding methods of the models in
769     the simulation.
770 jgs 147 """
771    
772     FAILED_TIME_STEPS_MAX=20
773     MAX_ITER_STEPS=50
774 gross 836 MAX_CHANGE_OF_DT=2.
775 jgs 147
776     def __init__(self, models=[], **kwargs):
777 jgs 149 """
778     Initiates a simulation from a list of models.
779     """
780 jgs 147 Model.__init__(self, **kwargs)
781     self.__models=[]
782 jgs 149
783 jgs 147 for i in range(len(models)):
784     self[i] = models[i]
785 jgs 149
786 jgs 121
787 jgs 147 def __repr__(self):
788     """
789 jgs 149 Returns a string representation of the Simulation.
790 jgs 147 """
791     return "<Simulation %r>" % self.__models
792 jgs 121
793 jgs 147 def __str__(self):
794     """
795 jgs 149 Returning Simulation as a string.
796 jgs 147 """
797     return "<Simulation %d>"%id(self)
798    
799     def iterModels(self):
800 jgs 149 """
801     Returns an iterator over the models.
802     """
803 jgs 147 return self.__models
804    
805     def __getitem__(self,i):
806 jgs 149 """
807     Returns the i-th model.
808     """
809 jgs 147 return self.__models[i]
810    
811     def __setitem__(self,i,value):
812 jgs 149 """
813     Sets the i-th model.
814     """
815 jgs 147 if not isinstance(value,Model):
816 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
817 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
818     self.__models.append(None)
819 jgs 147 self.__models[i]=value
820    
821     def __len__(self):
822 jgs 149 """
823     Returns the number of models.
824     """
825 jgs 147 return len(self.__models)
826 jgs 121
827 jgs 147 def toDom(self, document, node):
828 jgs 149 """
829     C{toDom} method of Simulation class.
830     """
831 jgs 147 simulation = document.createElement('Simulation')
832     simulation.setAttribute('type', self.__class__.__name__)
833 jgs 121
834 jgs 147 for rank, sim in enumerate(self.iterModels()):
835     component = document.createElement('Component')
836     component.setAttribute('rank', str(rank))
837 jgs 121
838 jgs 147 sim.toDom(document, component)
839 jgs 121
840 jgs 147 simulation.appendChild(component)
841 jgs 121
842 jgs 147 node.appendChild(simulation)
843 jgs 121
844 jgs 147 def writeXML(self,ostream=stdout):
845 jgs 149 """
846     Writes the object as an XML object into an output stream.
847     """
848 jgs 147 document, rootnode = esysDoc()
849     self.toDom(document, rootnode)
850 jgs 149 targetsList = document.getElementsByTagName('Target')
851 elspeth 282
852     for element in targetsList:
853     targetId = int(element.firstChild.nodeValue.strip())
854     if document.getElementById(str(targetId)):
855     continue
856 jgs 149 targetObj = LinkableObjectRegistry[targetId]
857     targetObj.toDom(document, rootnode)
858 jgs 147 ostream.write(document.toprettyxml())
859    
860     def getSafeTimeStepSize(self,dt):
861 jgs 149 """
862     Returns a time step size which can safely be used by all models.
863    
864     This is the minimum over the time step sizes of all models.
865     """
866 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
867     return out
868    
869     def doInitialization(self):
870 jgs 149 """
871     Initializes all models.
872     """
873 jgs 147 self.n=0
874     self.tn=0.
875     for o in self.iterModels():
876 gross 906 o.doInitialization()
877     def doInitialStep(self):
878     """
879     performs an iteration step in the initialization step for all models
880     """
881     for o in self.iterModels():
882     o.doInitialStep()
883    
884     def doInitialPostprocessing(self):
885     """
886     finalises the initialization iteration process for all models.
887     """
888     for o in self.iterModels():
889     o.doInitialPostprocessing()
890 jgs 147 def finalize(self):
891 jgs 149 """
892     Returns True if any of the models is to be finalized.
893     """
894 jgs 147 return any([o.finalize() for o in self.iterModels()])
895 jgs 123
896 jgs 147 def doFinalization(self):
897 jgs 149 """
898 gross 906 finalises the time stepping for all models.
899 jgs 149 """
900 jgs 147 for i in self.iterModels(): i.doFinalization()
901     self.trace("end of time integation.")
902    
903     def doStepPreprocessing(self,dt):
904 jgs 149 """
905     Initializes the time step for all models.
906     """
907 jgs 147 for o in self.iterModels():
908     o.doStepPreprocessing(dt)
909    
910     def terminateIteration(self):
911 jgs 149 """
912     Returns True if all iterations for all models are terminated.
913     """
914 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
915     return out
916 gross 906
917     def terminateInitialIteration(self):
918     """
919     Returns True if all initial iterations for all models are terminated.
920     """
921     out=all([o.terminateInitialIteration() for o in self.iterModels()])
922     return out
923 jgs 123
924 jgs 147 def doStepPostprocessing(self,dt):
925 jgs 149 """
926 gross 906 finalises the iteration process for all models.
927 jgs 149 """
928 jgs 147 for o in self.iterModels():
929     o.doStepPostprocessing(dt)
930     self.n+=1
931     self.tn+=dt
932    
933     def doStep(self,dt):
934     """
935 jgs 149 Executes the iteration step at a time step for all model::
936 jgs 147
937 jgs 149 self.doStepPreprocessing(dt)
938     while not self.terminateIteration():
939     for all models:
940     self.doStep(dt)
941     self.doStepPostprocessing(dt)
942 jgs 147 """
943     self.iter=0
944     while not self.terminateIteration():
945     if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
946     self.iter+=1
947     self.trace("iteration step %d"%(self.iter))
948     for o in self.iterModels():
949     o.doStep(dt)
950     if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
951 jgs 121
952 jgs 147 def run(self,check_point=None):
953     """
954 jgs 149 Run the simulation by performing essentially::
955 jgs 123
956 jgs 149 self.doInitialization()
957 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
958     self.doInitialPostprocessing()
959 jgs 149 while not self.finalize():
960     dt=self.getSafeTimeStepSize()
961     self.doStep(dt)
962     if n%check_point==0:
963     self.writeXML()
964     self.doFinalization()
965 jgs 121
966 jgs 149 If one of the models in throws a C{FailedTimeStepError} exception a
967     new time step size is computed through getSafeTimeStepSize() and the
968     time step is repeated.
969 jgs 121
970 jgs 149 If one of the models in throws a C{IterationDivergenceError}
971     exception the time step size is halved and the time step is repeated.
972 jgs 121
973 jgs 149 In both cases the time integration is given up after
974     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
975 jgs 147 """
976 gross 906 self.doInitialization()
977     iter=0
978     while not self.terminateInitialIteration():
979     self.doInitialStep()
980     iter+=1
981     if iter>self.MAX_ITER_STEPS:
982     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
983     self.doInitialPostprocessing()
984     self.trace("Initialization finalized after %s iteration steps."%iter)
985 jgs 147 dt=self.UNDEF_DT
986     while not self.finalize():
987     step_fail_counter=0
988     iteration_fail_counter=0
989 gross 836 if self.n==0:
990     dt_new=self.getSafeTimeStepSize(dt)
991     else:
992 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
993 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
994     end_of_step=False
995     while not end_of_step:
996     end_of_step=True
997     if not dt_new>0:
998 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
999 jgs 147 try:
1000     self.doStepPreprocessing(dt_new)
1001     self.doStep(dt_new)
1002     self.doStepPostprocessing(dt_new)
1003     except IterationDivergenceError:
1004     dt_new*=0.5
1005     end_of_step=False
1006     iteration_fail_counter+=1
1007     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1008 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1009     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1010 jgs 147 except FailedTimeStepError:
1011     dt_new=self.getSafeTimeStepSize(dt)
1012     end_of_step=False
1013     step_fail_counter+=1
1014 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1015 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1016 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1017 jgs 147 dt=dt_new
1018     if not check_point==None:
1019     if n%check_point==0:
1020     self.trace("check point is created.")
1021     self.writeXML()
1022     self.doFinalization()
1023 jgs 121
1024 jgs 147 def fromDom(cls, doc):
1025     sims = []
1026     for node in doc.childNodes:
1027     if isinstance(node, minidom.Text):
1028     continue
1029 jgs 121
1030 jgs 147 sims.append(getComponent(node))
1031 jgs 121
1032 jgs 147 return cls(sims)
1033 jgs 121
1034 jgs 147 fromDom = classmethod(fromDom)
1035 jgs 121
1036    
1037 jgs 147 class IterationDivergenceError(Exception):
1038     """
1039 jgs 149 Exception which is thrown if there is no convergence of the iteration
1040     process at a time step.
1041 jgs 121
1042 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1043 jgs 147 """
1044     pass
1045 jgs 121
1046 jgs 147 class FailedTimeStepError(Exception):
1047 jgs 149 """
1048     Exception which is thrown if the time step fails because of a step
1049     size that have been choosen to be too large.
1050     """
1051 jgs 147 pass
1052 jgs 121
1053 jgs 147 class SimulationBreakDownError(Exception):
1054 jgs 149 """
1055     Exception which is thrown if the simulation does not manage to
1056     progress in time.
1057     """
1058 jgs 147 pass
1059 jgs 121
1060 jgs 147 class NonPositiveStepSizeError(Exception):
1061 jgs 149 """
1062     Exception which is thrown if the step size is not positive.
1063     """
1064 jgs 147 pass
1065 jgs 149
1066 elspeth 875 class DataSource(object):
1067     """
1068     Class for handling data sources, including local and remote files. This class is under development.
1069     """
1070    
1071 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1072 elspeth 875 self.uri = uri
1073     self.fileformat = fileformat
1074    
1075     def toDom(self, document, node):
1076     """
1077     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1078     current XML document.
1079     """
1080     ds = document.createElement('DataSource')
1081     ds.appendChild(dataNode(document, 'URI', self.uri))
1082     ds.appendChild(dataNode(document, 'FileFormat', self.fileformat))
1083     node.appendChild(ds)
1084    
1085     def fromDom(cls, doc):
1086     uri= doc.getElementsByTagName("URI")[0].firstChild.nodeValue.strip()
1087     fileformat= doc.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip()
1088     ds = cls(uri, fileformat)
1089     return ds
1090    
1091 gross 885 def getLocalFileName(self):
1092     return self.uri
1093    
1094 elspeth 875 fromDom = classmethod(fromDom)
1095    
1096 jgs 149 # vim: expandtab shiftwidth=4:

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.26