/[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 926 - (show annotations)
Fri Jan 12 06:31:37 2007 UTC (12 years, 3 months ago) by gross
File MIME type: text/x-python
File size: 38095 byte(s)
modelframe support dictonaries with fixed type of key and value. only writing implemented yet
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 elif isinstance(value, dict):
574 dic = esysxml.createElement('dictionary')
575 if len(value.keys())>0:
576 dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
577 dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
578 for k,v in value.items():
579 i=esysxml.createElement('item')
580 i.appendChild(esysxml.createDataNode('key', k))
581 i.appendChild(esysxml.createDataNode('value', v))
582 dic.appendChild(i)
583 param.appendChild(dic)
584 else:
585 print value
586 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
587
588 node.appendChild(param)
589
590 def fromDom(cls, esysxml, node):
591 # Define a host of helper functions to assist us.
592 def _children(node):
593 """
594 Remove the empty nodes from the children of this node.
595 """
596 ret = []
597 for x in node.childNodes:
598 if isinstance(x, minidom.Text):
599 if x.nodeValue.strip():
600 ret.append(x)
601 else:
602 ret.append(x)
603 return ret
604
605 def _floatfromValue(esysxml, node):
606 return float(node.nodeValue.strip())
607
608 def _stringfromValue(esysxml, node):
609 return str(node.nodeValue.strip())
610
611 def _intfromValue(esysxml, node):
612 return int(node.nodeValue.strip())
613
614 def _boolfromValue(esysxml, node):
615 return _boolfromstring(node.nodeValue.strip())
616
617 def _nonefromValue(esysxml, node):
618 return None
619
620 def _numarrayfromValue(esysxml, node):
621 for node in _children(node):
622 if node.tagName == 'ArrayType':
623 arraytype = node.firstChild.nodeValue.strip()
624 if node.tagName == 'Shape':
625 shape = node.firstChild.nodeValue.strip()
626 shape = [int(x) for x in shape.split()]
627 if node.tagName == 'Data':
628 data = node.firstChild.nodeValue.strip()
629 data = [float(x) for x in data.split()]
630 return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
631 shape)
632
633 def _listfromValue(esysxml, node):
634 return [x for x in node.nodeValue.split()]
635
636 def _boolfromstring(s):
637 if s == 'True':
638 return True
639 else:
640 return False
641 # Mapping from text types in the xml to methods used to process trees of that type
642 ptypemap = {"Simulation": Simulation.fromDom,
643 "Model":Model.fromDom,
644 "ParameterSet":ParameterSet.fromDom,
645 "Link":Link.fromDom,
646 "DataSource":DataSource.fromDom,
647 "float":_floatfromValue,
648 "int":_intfromValue,
649 "str":_stringfromValue,
650 "bool":_boolfromValue,
651 "list":_listfromValue,
652 "NumArray":_numarrayfromValue,
653 "NoneType":_nonefromValue,
654 }
655
656 parameters = {}
657 for n in _children(node):
658 ptype = n.getAttribute("type")
659 if not ptypemap.has_key(ptype):
660 raise KeyError("cannot handle parameter type %s."%ptype)
661
662 pname = pvalue = None
663 for childnode in _children(n):
664 if childnode.tagName == "Name":
665 pname = childnode.firstChild.nodeValue.strip()
666
667 if childnode.tagName == "Value":
668 nodes = _children(childnode)
669 pvalue = ptypemap[ptype](esysxml, nodes[0])
670
671 parameters[pname] = pvalue
672
673 # Create the instance of ParameterSet
674 o = cls(debug=esysxml.debug)
675 o.declareParameters(parameters)
676 esysxml.registerLinkableObject(o, node)
677 return o
678
679 fromDom = classmethod(fromDom)
680
681 def writeXML(self,ostream=stdout):
682 """
683 Writes the object as an XML object into an output stream.
684 """
685 esysxml=ESySXMLCreator()
686 self.toDom(esysxml, esysxml.getRoot())
687 ostream.write(esysxml.toprettyxml())
688
689 class Model(ParameterSet):
690 """
691 A Model object represents a processess marching over time until a
692 finalizing condition is fullfilled. At each time step an iterative
693 process can be performed and the time step size can be controlled. A
694 Model has the following work flow::
695
696 doInitialization()
697 while not terminateInitialIteration(): doInitializationiStep()
698 doInitialPostprocessing()
699 while not finalize():
700 dt=getSafeTimeStepSize(dt)
701 doStepPreprocessing(dt)
702 while not terminateIteration(): doStep(dt)
703 doStepPostprocessing(dt)
704 doFinalization()
705
706 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
707 C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
708 C{doFinalization} are methods of the particular instance of a Model. The
709 default implementations of these methods have to be overwritten by the
710 subclass implementing a Model.
711 """
712
713 UNDEF_DT=1.e300
714
715 def __init__(self,parameters=[],**kwargs):
716 """
717 Creates a model.
718
719 Just calls the parent constructor.
720 """
721 ParameterSet.__init__(self, parameters=parameters,**kwargs)
722
723 def __str__(self):
724 return "<%s %d>"%(self.__class__.__name__,id(self))
725
726
727 def doInitialization(self):
728 """
729 Initializes the time stepping scheme.
730
731 This function may be overwritten.
732 """
733 pass
734 def doInitialStep(self):
735 """
736 performs an iteration step in the initialization phase
737
738 This function may be overwritten.
739 """
740 pass
741
742 def terminateInitialIteration(self):
743 """
744 Returns True if iteration at the inital phase is terminated.
745 """
746 return True
747
748 def doInitialPostprocessing(self):
749 """
750 finalises the initialization iteration process
751
752 This function may be overwritten.
753 """
754 pass
755
756 def getSafeTimeStepSize(self,dt):
757 """
758 Returns a time step size which can safely be used.
759
760 C{dt} gives the previously used step size.
761
762 This function may be overwritten.
763 """
764 return self.UNDEF_DT
765
766 def finalize(self):
767 """
768 Returns False if the time stepping is finalized.
769
770 This function may be overwritten.
771 """
772 return False
773
774 def doFinalization(self):
775 """
776 Finalizes the time stepping.
777
778 This function may be overwritten.
779 """
780 pass
781
782 def doStepPreprocessing(self,dt):
783 """
784 Sets up a time step of step size dt.
785
786 This function may be overwritten.
787 """
788 pass
789
790 def doStep(self,dt):
791 """
792 Executes an iteration step at a time step.
793
794 C{dt} is the currently used time step size.
795
796 This function may be overwritten.
797 """
798 pass
799
800 def terminateIteration(self):
801 """
802 Returns True if iteration on a time step is terminated.
803 """
804 return True
805
806
807 def doStepPostprocessing(self,dt):
808 """
809 finalises the time step.
810
811 dt is the currently used time step size.
812
813 This function may be overwritten.
814 """
815 pass
816
817 def toDom(self, esysxml, node):
818 """
819 C{toDom} method of Model class
820 """
821 pset = esysxml.createElement('Model')
822 pset.setAttribute('type', self.__class__.__name__)
823 pset.setAttribute('module', self.__class__.__module__)
824 esysxml.registerLinkableObject(self, pset)
825 node.appendChild(pset)
826 self._parametersToDom(esysxml, pset)
827
828 class Simulation(Model):
829 """
830 A Simulation object is special Model which runs a sequence of Models.
831
832 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
833 C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
834 C{doFinalization} are executing the corresponding methods of the models in
835 the simulation.
836 """
837
838 FAILED_TIME_STEPS_MAX=20
839 MAX_ITER_STEPS=50
840 MAX_CHANGE_OF_DT=2.
841
842 def __init__(self, models=[], **kwargs):
843 """
844 Initiates a simulation from a list of models.
845 """
846 super(Simulation, self).__init__(**kwargs)
847 self.__models=[]
848
849 for i in range(len(models)):
850 self[i] = models[i]
851
852
853 def __repr__(self):
854 """
855 Returns a string representation of the Simulation.
856 """
857 return "<Simulation %r>" % self.__models
858
859 def __str__(self):
860 """
861 Returning Simulation as a string.
862 """
863 return "<Simulation %d>"%id(self)
864
865 def iterModels(self):
866 """
867 Returns an iterator over the models.
868 """
869 return self.__models
870
871 def __getitem__(self,i):
872 """
873 Returns the i-th model.
874 """
875 return self.__models[i]
876
877 def __setitem__(self,i,value):
878 """
879 Sets the i-th model.
880 """
881 if not isinstance(value,Model):
882 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
883 for j in range(max(i-len(self.__models)+1,0)):
884 self.__models.append(None)
885 self.__models[i]=value
886
887 def __len__(self):
888 """
889 Returns the number of models.
890 """
891 return len(self.__models)
892
893
894 def getSafeTimeStepSize(self,dt):
895 """
896 Returns a time step size which can safely be used by all models.
897
898 This is the minimum over the time step sizes of all models.
899 """
900 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
901 return out
902
903 def doInitialization(self):
904 """
905 Initializes all models.
906 """
907 self.n=0
908 self.tn=0.
909 for o in self.iterModels():
910 o.doInitialization()
911 def doInitialStep(self):
912 """
913 performs an iteration step in the initialization step for all models
914 """
915 iter=0
916 while not self.terminateInitialIteration():
917 if iter==0: self.trace("iteration for initialization starts")
918 iter+=1
919 self.trace("iteration step %d"%(iter))
920 for o in self.iterModels():
921 o.doInitialStep()
922 if iter>self.MAX_ITER_STEPS:
923 raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
924 self.trace("Initialization finalized after %s iteration steps."%iter)
925
926 def doInitialPostprocessing(self):
927 """
928 finalises the initialization iteration process for all models.
929 """
930 for o in self.iterModels():
931 o.doInitialPostprocessing()
932 def finalize(self):
933 """
934 Returns True if any of the models is to be finalized.
935 """
936 return any([o.finalize() for o in self.iterModels()])
937
938 def doFinalization(self):
939 """
940 finalises the time stepping for all models.
941 """
942 for i in self.iterModels(): i.doFinalization()
943 self.trace("end of time integation.")
944
945 def doStepPreprocessing(self,dt):
946 """
947 Initializes the time step for all models.
948 """
949 for o in self.iterModels():
950 o.doStepPreprocessing(dt)
951
952 def terminateIteration(self):
953 """
954 Returns True if all iterations for all models are terminated.
955 """
956 out=all([o.terminateIteration() for o in self.iterModels()])
957 return out
958
959 def terminateInitialIteration(self):
960 """
961 Returns True if all initial iterations for all models are terminated.
962 """
963 out=all([o.terminateInitialIteration() for o in self.iterModels()])
964 return out
965
966 def doStepPostprocessing(self,dt):
967 """
968 finalises the iteration process for all models.
969 """
970 for o in self.iterModels():
971 o.doStepPostprocessing(dt)
972 self.n+=1
973 self.tn+=dt
974
975 def doStep(self,dt):
976 """
977 Executes the iteration step at a time step for all model::
978
979 self.doStepPreprocessing(dt)
980 while not self.terminateIteration():
981 for all models:
982 self.doStep(dt)
983 self.doStepPostprocessing(dt)
984 """
985 self.iter=0
986 while not self.terminateIteration():
987 if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
988 self.iter+=1
989 self.trace("iteration step %d"%(self.iter))
990 for o in self.iterModels():
991 o.doStep(dt)
992 if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
993
994 def run(self,check_point=None):
995 """
996 Run the simulation by performing essentially::
997
998 self.doInitialization()
999 while not self.terminateInitialIteration(): self.doInitialStep()
1000 self.doInitialPostprocessing()
1001 while not self.finalize():
1002 dt=self.getSafeTimeStepSize()
1003 self.doStep(dt)
1004 if n%check_point==0:
1005 self.writeXML()
1006 self.doFinalization()
1007
1008 If one of the models in throws a C{FailedTimeStepError} exception a
1009 new time step size is computed through getSafeTimeStepSize() and the
1010 time step is repeated.
1011
1012 If one of the models in throws a C{IterationDivergenceError}
1013 exception the time step size is halved and the time step is repeated.
1014
1015 In both cases the time integration is given up after
1016 C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1017 """
1018 self.doInitialization()
1019 self.doInitialStep()
1020 self.doInitialPostprocessing()
1021 dt=self.UNDEF_DT
1022 while not self.finalize():
1023 step_fail_counter=0
1024 iteration_fail_counter=0
1025 if self.n==0:
1026 dt_new=self.getSafeTimeStepSize(dt)
1027 else:
1028 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1029 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1030 end_of_step=False
1031 while not end_of_step:
1032 end_of_step=True
1033 if not dt_new>0:
1034 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1035 try:
1036 self.doStepPreprocessing(dt_new)
1037 self.doStep(dt_new)
1038 self.doStepPostprocessing(dt_new)
1039 except IterationDivergenceError:
1040 dt_new*=0.5
1041 end_of_step=False
1042 iteration_fail_counter+=1
1043 if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1044 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1045 self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1046 except FailedTimeStepError:
1047 dt_new=self.getSafeTimeStepSize(dt)
1048 end_of_step=False
1049 step_fail_counter+=1
1050 self.trace("Time step is repeated with new time step size %s."%dt_new)
1051 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1052 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1053 dt=dt_new
1054 if not check_point==None:
1055 if n%check_point==0:
1056 self.trace("check point is created.")
1057 self.writeXML()
1058 self.doFinalization()
1059
1060
1061 def toDom(self, esysxml, node):
1062 """
1063 C{toDom} method of Simulation class.
1064 """
1065 simulation = esysxml.createElement('Simulation')
1066 esysxml.registerLinkableObject(self, simulation)
1067 for rank, sim in enumerate(self.iterModels()):
1068 component = esysxml.createElement('Component')
1069 component.setAttribute('rank', str(rank))
1070 sim.toDom(esysxml, component)
1071 simulation.appendChild(component)
1072 node.appendChild(simulation)
1073
1074
1075 def fromDom(cls, esysxml, node):
1076 sims = []
1077 for n in node.childNodes:
1078 if isinstance(n, minidom.Text):
1079 continue
1080 sims.append(esysxml.getComponent(n))
1081 sims.sort(cmp=_comp)
1082 sim=cls([s[1] for s in sims], debug=esysxml.debug)
1083 esysxml.registerLinkableObject(sim, node)
1084 return sim
1085
1086 fromDom = classmethod(fromDom)
1087
1088 def _comp(a,b):
1089 if a[0]<a[1]:
1090 return 1
1091 elif a[0]>a[1]:
1092 return -1
1093 else:
1094 return 0
1095
1096 class IterationDivergenceError(Exception):
1097 """
1098 Exception which is thrown if there is no convergence of the iteration
1099 process at a time step.
1100
1101 But there is a chance that a smaller step could help to reach convergence.
1102 """
1103 pass
1104
1105 class FailedTimeStepError(Exception):
1106 """
1107 Exception which is thrown if the time step fails because of a step
1108 size that have been choosen to be too large.
1109 """
1110 pass
1111
1112 class SimulationBreakDownError(Exception):
1113 """
1114 Exception which is thrown if the simulation does not manage to
1115 progress in time.
1116 """
1117 pass
1118
1119 class NonPositiveStepSizeError(Exception):
1120 """
1121 Exception which is thrown if the step size is not positive.
1122 """
1123 pass
1124
1125 class DataSource(object):
1126 """
1127 Class for handling data sources, including local and remote files. This class is under development.
1128 """
1129
1130 def __init__(self, uri="file.ext", fileformat="unknown"):
1131 self.uri = uri
1132 self.fileformat = fileformat
1133
1134 def toDom(self, esysxml, node):
1135 """
1136 C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1137 current XML esysxml.
1138 """
1139 ds = esysxml.createElement('DataSource')
1140 ds.appendChild(esysxml.createDataNode('URI', self.uri))
1141 ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1142 node.appendChild(ds)
1143
1144 def fromDom(cls, esysxml, node):
1145 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1146 fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1147 ds = cls(uri, fileformat)
1148 return ds
1149
1150 def getLocalFileName(self):
1151 return self.uri
1152
1153 fromDom = classmethod(fromDom)
1154
1155 # 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