/[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 908 - (hide annotations)
Thu Nov 23 06:39:39 2006 UTC (12 years, 4 months ago) by gross
File MIME type: text/x-python
File size: 34448 byte(s)
some 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 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
401 jgs 123
402     def declareParameter(self,**parameters):
403 jgs 149 """
404     Declares a new parameter(s) and its (their) initial value.
405     """
406 jgs 123 self.declareParameters(parameters)
407    
408     def declareParameters(self,parameters):
409 jgs 149 """
410     Declares a set of parameters. parameters can be a list, a dictionary
411     or a ParameterSet.
412     """
413 jgs 123 if isinstance(parameters,ListType):
414     parameters = zip(parameters, itertools.repeat(None))
415     if isinstance(parameters,DictType):
416     parameters = parameters.iteritems()
417 jgs 121
418 jgs 123 for prm, value in parameters:
419     setattr(self,prm,value)
420     self.parameters.add(prm)
421 jgs 121
422 jgs 147 self.trace("parameter %s has been declared."%prm)
423 jgs 121
424 jgs 123 def releaseParameters(self,name):
425 jgs 149 """
426     Removes parameter name from the paramameters.
427     """
428 jgs 123 if self.isParameter(name):
429     self.parameters.remove(name)
430 jgs 147 self.trace("parameter %s has been removed."%name)
431 jgs 123
432     def __iter__(self):
433 jgs 149 """
434     Creates an iterator over the parameter and their values.
435     """
436 jgs 123 return _ParameterIterator(self)
437    
438     def showParameters(self):
439 jgs 149 """
440     Returns a descrition of the parameters.
441     """
442 jgs 123 out="{"
443     notfirst=False
444     for i,v in self:
445     if notfirst: out=out+","
446     notfirst=True
447     if isinstance(v,ParameterSet):
448     out="%s\"%s\" : %s"%(out,i,v.showParameters())
449     else:
450     out="%s\"%s\" : %s"%(out,i,v)
451     return out+"}"
452    
453     def __delattr__(self,name):
454 jgs 149 """
455     Removes the attribute name.
456     """
457 jgs 123 LinkableObject.__delattr__(self,name)
458     try:
459     self.releaseParameter(name)
460     except:
461     pass
462 jgs 121
463 jgs 123 def toDom(self, document, node):
464 jgs 149 """
465     C{toDom} method of ParameterSet class.
466     """
467 jgs 123 pset = document.createElement('ParameterSet')
468     node.appendChild(pset)
469     self._parametersToDom(document, pset)
470 jgs 121
471 jgs 123 def _parametersToDom(self, document, node):
472 elspeth 282 node.setAttribute('id', str(self.id))
473     node.setIdAttribute("id")
474 jgs 123 for name,value in self:
475     param = document.createElement('Parameter')
476     param.setAttribute('type', value.__class__.__name__)
477 jgs 121
478 jgs 123 param.appendChild(dataNode(document, 'Name', name))
479 jgs 121
480 jgs 123 val = document.createElement('Value')
481    
482 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
483 jgs 123 value.toDom(document, val)
484     param.appendChild(val)
485 elspeth 871 elif isinstance(value, numarray.NumArray):
486     shape = value.getshape()
487     if isinstance(shape, tuple):
488     size = reduce(operator.mul, shape)
489     shape = ' '.join(map(str, shape))
490     else:
491     size = shape
492     shape = str(shape)
493    
494     arraytype = value.type()
495 elspeth 874 numarrayElement = document.createElement('NumArray')
496     numarrayElement.appendChild(dataNode(document, 'ArrayType', str(arraytype)))
497     numarrayElement.appendChild(dataNode(document, 'Shape', shape))
498     numarrayElement.appendChild(dataNode(document, 'Data', ' '.join(
499 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
500 elspeth 874 val.appendChild(numarrayElement)
501 elspeth 871 param.appendChild(val)
502 elspeth 874 elif isinstance (value, list):
503     param.appendChild(dataNode(document, 'Value', ' '.join(
504     [str(x) for x in value])
505     ))
506 jgs 123 else:
507     param.appendChild(dataNode(document, 'Value', str(value)))
508    
509     node.appendChild(param)
510    
511     def fromDom(cls, doc):
512    
513     # Define a host of helper functions to assist us.
514     def _children(node):
515     """
516 jgs 149 Remove the empty nodes from the children of this node.
517 jgs 123 """
518 elspeth 871 ret = []
519     for x in node.childNodes:
520     if isinstance(x, minidom.Text):
521     if x.nodeValue.strip():
522     ret.append(x)
523     else:
524     ret.append(x)
525     return ret
526 jgs 123
527     def _floatfromValue(doc):
528     return float(doc.nodeValue.strip())
529    
530     def _stringfromValue(doc):
531     return str(doc.nodeValue.strip())
532 jgs 126
533     def _intfromValue(doc):
534     return int(doc.nodeValue.strip())
535    
536     def _boolfromValue(doc):
537 elspeth 874 return _boolfromstring(doc.nodeValue.strip())
538 elspeth 266
539     def _nonefromValue(doc):
540     return None
541 elspeth 871
542     def _numarrayfromValue(doc):
543 elspeth 874 for node in _children(doc):
544 elspeth 871 if node.tagName == 'ArrayType':
545     arraytype = node.firstChild.nodeValue.strip()
546     if node.tagName == 'Shape':
547     shape = node.firstChild.nodeValue.strip()
548     shape = [int(x) for x in shape.split()]
549     if node.tagName == 'Data':
550     data = node.firstChild.nodeValue.strip()
551     data = [float(x) for x in data.split()]
552     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
553     shape)
554 elspeth 874
555     def _listfromValue(doc):
556     return [_boolfromstring(x) for x in doc.nodeValue.split()]
557    
558    
559     def _boolfromstring(s):
560     if s == 'True':
561     return True
562     else:
563     return False
564 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
565     ptypemap = {"Simulation": Simulation.fromDom,
566     "Model":Model.fromDom,
567     "ParameterSet":ParameterSet.fromDom,
568     "Link":Link.fromDom,
569 elspeth 875 "DataSource":DataSource.fromDom,
570 jgs 123 "float":_floatfromValue,
571 jgs 126 "int":_intfromValue,
572 jgs 123 "str":_stringfromValue,
573 elspeth 269 "bool":_boolfromValue,
574 elspeth 874 "list":_listfromValue,
575     "NumArray":_numarrayfromValue,
576 elspeth 871 "NoneType":_nonefromValue,
577 jgs 123 }
578    
579 jgs 147 # print doc.toxml()
580    
581 jgs 123 parameters = {}
582     for node in _children(doc):
583     ptype = node.getAttribute("type")
584    
585     pname = pvalue = None
586     for childnode in _children(node):
587    
588     if childnode.tagName == "Name":
589     pname = childnode.firstChild.nodeValue.strip()
590    
591     if childnode.tagName == "Value":
592     nodes = _children(childnode)
593 elspeth 874 # if ptype == 'NumArray':
594     # pvalue = _numarrayfromValue(nodes)
595     # else:
596     pvalue = ptypemap[ptype](nodes[0])
597 jgs 123
598     parameters[pname] = pvalue
599    
600     # Create the instance of ParameterSet
601     o = cls()
602     o.declareParameters(parameters)
603     registerLinkableObject(doc.getAttribute("id"), o)
604     return o
605    
606     fromDom = classmethod(fromDom)
607    
608     def writeXML(self,ostream=stdout):
609 jgs 149 """
610     Writes the object as an XML object into an output stream.
611     """
612 jgs 123 # ParameterSet(d) with d[Name]=Value
613     document, node = esysDoc()
614     self.toDom(document, node)
615     ostream.write(document.toprettyxml())
616    
617 jgs 147 class Model(ParameterSet):
618     """
619 jgs 149 A Model object represents a processess marching over time until a
620     finalizing condition is fullfilled. At each time step an iterative
621     process can be performed and the time step size can be controlled. A
622     Model has the following work flow::
623 gross 906
624 jgs 147 doInitialization()
625 gross 906 while not terminateInitialIteration(): doInitializationiStep()
626     doInitialPostprocessing()
627 jgs 147 while not finalize():
628     dt=getSafeTimeStepSize(dt)
629     doStepPreprocessing(dt)
630     while not terminateIteration(): doStep(dt)
631     doStepPostprocessing(dt)
632     doFinalization()
633 jgs 121
634 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
635     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
636     C{doFinalization} are methods of the particular instance of a Model. The
637     default implementations of these methods have to be overwritten by the
638     subclass implementing a Model.
639 jgs 147 """
640 jgs 121
641 jgs 147 UNDEF_DT=1.e300
642 jgs 121
643 jgs 147 def __init__(self,parameters=[],**kwarg):
644 jgs 149 """
645     Creates a model.
646 jgs 121
647 jgs 149 Just calls the parent constructor.
648 jgs 147 """
649     ParameterSet.__init__(self, parameters=parameters,**kwarg)
650 jgs 121
651 jgs 147 def __str__(self):
652 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
653 jgs 121
654 jgs 147 def toDom(self, document, node):
655 jgs 149 """
656     C{toDom} method of Model class
657     """
658 jgs 147 pset = document.createElement('Model')
659     pset.setAttribute('type', self.__class__.__name__)
660 jgs 150 if not self.__class__.__module__.startswith('esys.escript'):
661     pset.setAttribute('module', self.__class__.__module__)
662 jgs 147 node.appendChild(pset)
663     self._parametersToDom(document, pset)
664 jgs 121
665 jgs 147 def doInitialization(self):
666 jgs 149 """
667     Initializes the time stepping scheme.
668    
669     This function may be overwritten.
670     """
671 jgs 147 pass
672 gross 906 def doInitialStep(self):
673     """
674     performs an iteration step in the initialization phase
675    
676     This function may be overwritten.
677     """
678     pass
679    
680     def terminateInitialIteration(self):
681     """
682     Returns True if iteration at the inital phase is terminated.
683     """
684     return True
685    
686     def doInitialPostprocessing(self):
687     """
688     finalises the initialization iteration process
689    
690     This function may be overwritten.
691     """
692     pass
693 jgs 147
694     def getSafeTimeStepSize(self,dt):
695 jgs 149 """
696     Returns a time step size which can safely be used.
697    
698     C{dt} gives the previously used step size.
699    
700     This function may be overwritten.
701     """
702 jgs 147 return self.UNDEF_DT
703    
704     def finalize(self):
705 jgs 149 """
706     Returns False if the time stepping is finalized.
707    
708     This function may be overwritten.
709     """
710 jgs 147 return False
711 jgs 123
712 jgs 147 def doFinalization(self):
713 jgs 149 """
714     Finalizes the time stepping.
715    
716     This function may be overwritten.
717     """
718 jgs 147 pass
719    
720     def doStepPreprocessing(self,dt):
721 jgs 149 """
722     Sets up a time step of step size dt.
723    
724     This function may be overwritten.
725     """
726 jgs 147 pass
727    
728     def doStep(self,dt):
729 jgs 149 """
730     Executes an iteration step at a time step.
731    
732     C{dt} is the currently used time step size.
733    
734     This function may be overwritten.
735     """
736 jgs 147 pass
737    
738     def terminateIteration(self):
739 jgs 149 """
740     Returns True if iteration on a time step is terminated.
741     """
742 jgs 147 return True
743 gross 906
744 jgs 147
745     def doStepPostprocessing(self,dt):
746 jgs 149 """
747 gross 906 finalises the time step.
748 jgs 149
749     dt is the currently used time step size.
750    
751     This function may be overwritten.
752     """
753 jgs 147 pass
754    
755     def writeXML(self, ostream=stdout):
756     document, node = esysDoc()
757     self.toDom(document, node)
758     ostream.write(document.toprettyxml())
759    
760 jgs 121
761 jgs 147 class Simulation(Model):
762     """
763 jgs 149 A Simulation object is special Model which runs a sequence of Models.
764 jgs 121
765 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
766     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
767     C{doFinalization} are executing the corresponding methods of the models in
768     the simulation.
769 jgs 147 """
770    
771     FAILED_TIME_STEPS_MAX=20
772     MAX_ITER_STEPS=50
773 gross 836 MAX_CHANGE_OF_DT=2.
774 jgs 147
775     def __init__(self, models=[], **kwargs):
776 jgs 149 """
777     Initiates a simulation from a list of models.
778     """
779 jgs 147 Model.__init__(self, **kwargs)
780     self.__models=[]
781 jgs 149
782 jgs 147 for i in range(len(models)):
783     self[i] = models[i]
784 jgs 149
785 jgs 121
786 jgs 147 def __repr__(self):
787     """
788 jgs 149 Returns a string representation of the Simulation.
789 jgs 147 """
790     return "<Simulation %r>" % self.__models
791 jgs 121
792 jgs 147 def __str__(self):
793     """
794 jgs 149 Returning Simulation as a string.
795 jgs 147 """
796     return "<Simulation %d>"%id(self)
797    
798     def iterModels(self):
799 jgs 149 """
800     Returns an iterator over the models.
801     """
802 jgs 147 return self.__models
803    
804     def __getitem__(self,i):
805 jgs 149 """
806     Returns the i-th model.
807     """
808 jgs 147 return self.__models[i]
809    
810     def __setitem__(self,i,value):
811 jgs 149 """
812     Sets the i-th model.
813     """
814 jgs 147 if not isinstance(value,Model):
815 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
816 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
817     self.__models.append(None)
818 jgs 147 self.__models[i]=value
819    
820     def __len__(self):
821 jgs 149 """
822     Returns the number of models.
823     """
824 jgs 147 return len(self.__models)
825 jgs 121
826 jgs 147 def toDom(self, document, node):
827 jgs 149 """
828     C{toDom} method of Simulation class.
829     """
830 jgs 147 simulation = document.createElement('Simulation')
831     simulation.setAttribute('type', self.__class__.__name__)
832 jgs 121
833 jgs 147 for rank, sim in enumerate(self.iterModels()):
834     component = document.createElement('Component')
835     component.setAttribute('rank', str(rank))
836 jgs 121
837 jgs 147 sim.toDom(document, component)
838 jgs 121
839 jgs 147 simulation.appendChild(component)
840 jgs 121
841 jgs 147 node.appendChild(simulation)
842 jgs 121
843 jgs 147 def writeXML(self,ostream=stdout):
844 jgs 149 """
845     Writes the object as an XML object into an output stream.
846     """
847 jgs 147 document, rootnode = esysDoc()
848     self.toDom(document, rootnode)
849 jgs 149 targetsList = document.getElementsByTagName('Target')
850 elspeth 282
851     for element in targetsList:
852     targetId = int(element.firstChild.nodeValue.strip())
853     if document.getElementById(str(targetId)):
854     continue
855 jgs 149 targetObj = LinkableObjectRegistry[targetId]
856     targetObj.toDom(document, rootnode)
857 jgs 147 ostream.write(document.toprettyxml())
858    
859     def getSafeTimeStepSize(self,dt):
860 jgs 149 """
861     Returns a time step size which can safely be used by all models.
862    
863     This is the minimum over the time step sizes of all models.
864     """
865 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
866     return out
867    
868     def doInitialization(self):
869 jgs 149 """
870     Initializes all models.
871     """
872 jgs 147 self.n=0
873     self.tn=0.
874     for o in self.iterModels():
875 gross 906 o.doInitialization()
876     def doInitialStep(self):
877     """
878     performs an iteration step in the initialization step for all models
879     """
880 gross 908 iter=0
881     while not self.terminateInitialIteration():
882     if iter==0: self.trace("iteration for initialization starts")
883     iter+=1
884     self.trace("iteration step %d"%(iter))
885     for o in self.iterModels():
886     o.doInitialStep()
887     if iter>self.MAX_ITER_STEPS:
888     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
889     self.trace("Initialization finalized after %s iteration steps."%iter)
890 gross 906
891     def doInitialPostprocessing(self):
892     """
893     finalises the initialization iteration process for all models.
894     """
895     for o in self.iterModels():
896     o.doInitialPostprocessing()
897 jgs 147 def finalize(self):
898 jgs 149 """
899     Returns True if any of the models is to be finalized.
900     """
901 jgs 147 return any([o.finalize() for o in self.iterModels()])
902 jgs 123
903 jgs 147 def doFinalization(self):
904 jgs 149 """
905 gross 906 finalises the time stepping for all models.
906 jgs 149 """
907 jgs 147 for i in self.iterModels(): i.doFinalization()
908     self.trace("end of time integation.")
909    
910     def doStepPreprocessing(self,dt):
911 jgs 149 """
912     Initializes the time step for all models.
913     """
914 jgs 147 for o in self.iterModels():
915     o.doStepPreprocessing(dt)
916    
917     def terminateIteration(self):
918 jgs 149 """
919     Returns True if all iterations for all models are terminated.
920     """
921 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
922     return out
923 gross 906
924     def terminateInitialIteration(self):
925     """
926     Returns True if all initial iterations for all models are terminated.
927     """
928     out=all([o.terminateInitialIteration() for o in self.iterModels()])
929     return out
930 jgs 123
931 jgs 147 def doStepPostprocessing(self,dt):
932 jgs 149 """
933 gross 906 finalises the iteration process for all models.
934 jgs 149 """
935 jgs 147 for o in self.iterModels():
936     o.doStepPostprocessing(dt)
937     self.n+=1
938     self.tn+=dt
939    
940     def doStep(self,dt):
941     """
942 jgs 149 Executes the iteration step at a time step for all model::
943 jgs 147
944 jgs 149 self.doStepPreprocessing(dt)
945     while not self.terminateIteration():
946     for all models:
947     self.doStep(dt)
948     self.doStepPostprocessing(dt)
949 jgs 147 """
950     self.iter=0
951     while not self.terminateIteration():
952     if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
953     self.iter+=1
954     self.trace("iteration step %d"%(self.iter))
955     for o in self.iterModels():
956     o.doStep(dt)
957     if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
958 jgs 121
959 jgs 147 def run(self,check_point=None):
960     """
961 jgs 149 Run the simulation by performing essentially::
962 jgs 123
963 jgs 149 self.doInitialization()
964 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
965     self.doInitialPostprocessing()
966 jgs 149 while not self.finalize():
967     dt=self.getSafeTimeStepSize()
968     self.doStep(dt)
969     if n%check_point==0:
970     self.writeXML()
971     self.doFinalization()
972 jgs 121
973 jgs 149 If one of the models in throws a C{FailedTimeStepError} exception a
974     new time step size is computed through getSafeTimeStepSize() and the
975     time step is repeated.
976 jgs 121
977 jgs 149 If one of the models in throws a C{IterationDivergenceError}
978     exception the time step size is halved and the time step is repeated.
979 jgs 121
980 jgs 149 In both cases the time integration is given up after
981     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
982 jgs 147 """
983 gross 906 self.doInitialization()
984 gross 908 self.doInitialStep()
985 gross 906 self.doInitialPostprocessing()
986 jgs 147 dt=self.UNDEF_DT
987     while not self.finalize():
988     step_fail_counter=0
989     iteration_fail_counter=0
990 gross 836 if self.n==0:
991     dt_new=self.getSafeTimeStepSize(dt)
992     else:
993 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
994 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
995     end_of_step=False
996     while not end_of_step:
997     end_of_step=True
998     if not dt_new>0:
999 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1000 jgs 147 try:
1001     self.doStepPreprocessing(dt_new)
1002     self.doStep(dt_new)
1003     self.doStepPostprocessing(dt_new)
1004     except IterationDivergenceError:
1005     dt_new*=0.5
1006     end_of_step=False
1007     iteration_fail_counter+=1
1008     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1009 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1010     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1011 jgs 147 except FailedTimeStepError:
1012     dt_new=self.getSafeTimeStepSize(dt)
1013     end_of_step=False
1014     step_fail_counter+=1
1015 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1016 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1017 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1018 jgs 147 dt=dt_new
1019     if not check_point==None:
1020     if n%check_point==0:
1021     self.trace("check point is created.")
1022     self.writeXML()
1023     self.doFinalization()
1024 jgs 121
1025 jgs 147 def fromDom(cls, doc):
1026     sims = []
1027     for node in doc.childNodes:
1028     if isinstance(node, minidom.Text):
1029     continue
1030 jgs 121
1031 jgs 147 sims.append(getComponent(node))
1032 jgs 121
1033 jgs 147 return cls(sims)
1034 jgs 121
1035 jgs 147 fromDom = classmethod(fromDom)
1036 jgs 121
1037    
1038 jgs 147 class IterationDivergenceError(Exception):
1039     """
1040 jgs 149 Exception which is thrown if there is no convergence of the iteration
1041     process at a time step.
1042 jgs 121
1043 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1044 jgs 147 """
1045     pass
1046 jgs 121
1047 jgs 147 class FailedTimeStepError(Exception):
1048 jgs 149 """
1049     Exception which is thrown if the time step fails because of a step
1050     size that have been choosen to be too large.
1051     """
1052 jgs 147 pass
1053 jgs 121
1054 jgs 147 class SimulationBreakDownError(Exception):
1055 jgs 149 """
1056     Exception which is thrown if the simulation does not manage to
1057     progress in time.
1058     """
1059 jgs 147 pass
1060 jgs 121
1061 jgs 147 class NonPositiveStepSizeError(Exception):
1062 jgs 149 """
1063     Exception which is thrown if the step size is not positive.
1064     """
1065 jgs 147 pass
1066 jgs 149
1067 elspeth 875 class DataSource(object):
1068     """
1069     Class for handling data sources, including local and remote files. This class is under development.
1070     """
1071    
1072 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1073 elspeth 875 self.uri = uri
1074     self.fileformat = fileformat
1075    
1076     def toDom(self, document, node):
1077     """
1078     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1079     current XML document.
1080     """
1081     ds = document.createElement('DataSource')
1082     ds.appendChild(dataNode(document, 'URI', self.uri))
1083     ds.appendChild(dataNode(document, 'FileFormat', self.fileformat))
1084     node.appendChild(ds)
1085    
1086     def fromDom(cls, doc):
1087     uri= doc.getElementsByTagName("URI")[0].firstChild.nodeValue.strip()
1088     fileformat= doc.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip()
1089     ds = cls(uri, fileformat)
1090     return ds
1091    
1092 gross 885 def getLocalFileName(self):
1093     return self.uri
1094    
1095 elspeth 875 fromDom = classmethod(fromDom)
1096    
1097 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