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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 918 - (show annotations)
Wed Jan 3 06:30:00 2007 UTC (12 years, 11 months ago) by gross
File MIME type: text/x-python
File size: 37422 byte(s)
fixes on ESySXML to get runmodel going.

    * object ids are now local for read and write of XML
    * ParameterSets are now written with class name
    * ParameterSets linked by other ParameterSets (previously only Models) are written now 
    * list are now lists of str (rather than bools), lists with bool, int or float are mapped to numarray
    * numarray are writen with generic types Bool, Int, Float (portability!)


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