/[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 952 - (show annotations)
Wed Feb 7 23:53:24 2007 UTC (11 years, 11 months ago) by gross
File MIME type: text/x-python
File size: 40650 byte(s)
testing of simulation completness improved.
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.getElementsByTagName('ESys')[0]
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 getTarget(self):
218 """
219 returns the target
220 """
221 return self.target
222 def getAttributeName(self):
223 """
224 returns the name of the attribute the link is pointing to
225 """
226 return self.attribute
227
228 def setAttributeName(self,attribute):
229 """
230 Set a new attribute name to be collected from the target object. The
231 target object must have the attribute with name attribute.
232 """
233 if attribute and self.target:
234 if isinstance(self.target,LinkableObject):
235 if not self.target.hasAttribute(attribute):
236 raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
237 else:
238 if not hasattr(self.target,attribute):
239 raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
240 self.attribute = attribute
241
242 def hasDefinedAttributeName(self):
243 """
244 Returns true if an attribute name is set.
245 """
246 return self.attribute != None
247
248 def __repr__(self):
249 """
250 Returns a string representation of the link.
251 """
252 if self.hasDefinedAttributeName():
253 return "<Link to attribute %s of %s>" % (self.attribute, self.target)
254 else:
255 return "<Link to target %s>" % self.target
256
257 def __call__(self,name=None):
258 """
259 Returns the value of the attribute of the target object. If the
260 atrribute is callable then the return value of the call is returned.
261 """
262 if name:
263 out=getattr(self.target, name)
264 else:
265 out=getattr(self.target, self.attribute)
266
267 if callable(out):
268 return out()
269 else:
270 return out
271
272 def toDom(self, esysxml, node):
273 """
274 C{toDom} method of Link. Creates a Link node and appends it to the
275 current XML esysxml.
276 """
277 link = esysxml.createElement('Link')
278 assert (self.target != None), ("Target was none, name was %r" % self.attribute)
279 link.appendChild(esysxml.createDataNode('Target', esysxml.getLinkableObjectId(self.target)))
280 # this use of id will not work for purposes of being able to retrieve the intended
281 # target from the xml later. I need a better unique identifier.
282 assert self.attribute, "You can't xmlify a Link without a target attribute"
283 link.appendChild(esysxml.createDataNode('Attribute', self.attribute))
284 node.appendChild(link)
285
286 def fromDom(cls, esysxml, node):
287 targetid = int(node.getElementsByTagName("Target")[0].firstChild.nodeValue.strip())
288 attribute =str(node.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip())
289 l = cls(None, attribute)
290 esysxml.registerLink(targetid, l)
291 return l
292
293 fromDom = classmethod(fromDom)
294
295 class LinkableObject(object):
296 """
297 An object that allows to link its attributes to attributes of other objects
298 via a Link object. For instance::
299
300 p = LinkableObject()
301 p.x = Link(o,"name")
302 print p.x
303
304 links attribute C{x} of C{p} to the attribute name of object C{o}.
305
306 C{p.x} will contain the current value of attribute C{name} of object
307 C{o}.
308
309 If the value of C{getattr(o, "name")} is callable, C{p.x} will return
310 the return value of the call.
311 """
312
313
314 def __init__(self, id = None, debug=False):
315 """
316 Initializes LinkableObject so that we can operate on Links
317 """
318 self.debug = debug
319 self.__linked_attributes={}
320
321 def trace(self, msg):
322 """
323 If debugging is on, print the message, otherwise do nothing
324 """
325 if self.debug:
326 print "%s: %s"%(str(self),msg)
327
328 def __getattr__(self,name):
329 """
330 Returns the value of attribute name. If the value is a Link object the
331 object is called and the return value is returned.
332 """
333 out = self.getAttributeObject(name)
334 if isinstance(out,Link):
335 return out()
336 else:
337 return out
338
339 def getAttributeObject(self,name):
340 """
341 Return the object stored for attribute name.
342 """
343
344 if self.__dict__.has_key(name):
345 return self.__dict__[name]
346
347 if self.__linked_attributes.has_key(name):
348 return self.__linked_attributes[name]
349
350 if self.__class__.__dict__.has_key(name):
351 return self.__class.__dict__[name]
352
353 raise AttributeError,"No attribute %s."%name
354
355 def hasAttribute(self,name):
356 """
357 Returns True if self as attribute name.
358 """
359 return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name)
360
361 def __setattr__(self,name,value):
362 """
363 Sets the value for attribute name. If value is a Link the target
364 attribute is set to name if no attribute has been specified.
365 """
366
367 if self.__dict__.has_key(name):
368 del self.__dict__[name]
369
370 if isinstance(value,Link):
371 if not value.hasDefinedAttributeName():
372 value.setAttributeName(name)
373 self.__linked_attributes[name] = value
374
375 self.trace("attribute %s is now linked by %s."%(name,value))
376 else:
377 self.__dict__[name] = value
378
379 def __delattr__(self,name):
380 """
381 Removes the attribute name.
382 """
383
384 if self.__linked_attributes.has_key[name]:
385 del self.__linked_attributes[name]
386 elif self.__dict__.has_key(name):
387 del self.__dict__[name]
388 else:
389 raise AttributeError,"No attribute %s."%name
390
391 class _ParameterIterator:
392 def __init__(self,parameterset):
393
394 self.__set=parameterset
395 self.__iter=iter(parameterset.parameters)
396
397 def next(self):
398 o=self.__iter.next()
399 return (o,self.__set.getAttributeObject(o))
400
401 def __iter__(self):
402 return self
403
404 class ParameterSet(LinkableObject):
405 """
406 A class which allows to emphazise attributes to be written and read to XML
407
408 Leaves of an ESySParameters object can be:
409
410 - a real number
411 - a integer number
412 - a string
413 - a boolean value
414 - a ParameterSet object
415 - a Simulation object
416 - a Model object
417 - a numarray object
418 - a list of booleans
419 - any other object (not considered by writeESySXML and writeXML)
420
421 Example how to create an ESySParameters object::
422
423 p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.)
424 p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11)
425 parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1")))
426
427 This can be accessed as::
428
429 parm.parm1.gamma=0.
430 parm.parm1.dim=2
431 parm.parm1.tol_v=0.001
432 parm.parm1.output_file="/tmp/u.%3.3d.dx"
433 parm.parm1.runFlag=True
434 parm.parm1.parm11.gamma1=1.
435 parm.parm1.parm11.gamma2=2.
436 parm.parm1.parm11.gamma3=3.
437 parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1)
438 """
439 def __init__(self, parameters=[], **kwargs):
440 """
441 Creates a ParameterSet with parameters parameters.
442 """
443 LinkableObject.__init__(self, **kwargs)
444 self.parameters = set()
445 self.declareParameters(parameters)
446
447 def __repr__(self):
448 return "<%s %d>"%(self.__class__.__name__,id(self))
449
450 def declareParameter(self,**parameters):
451 """
452 Declares a new parameter(s) and its (their) initial value.
453 """
454 self.declareParameters(parameters)
455
456 def declareParameters(self,parameters):
457 """
458 Declares a set of parameters. parameters can be a list, a dictionary
459 or a ParameterSet.
460 """
461 if isinstance(parameters,ListType):
462 parameters = zip(parameters, itertools.repeat(None))
463 if isinstance(parameters,DictType):
464 parameters = parameters.iteritems()
465
466 for prm, value in parameters:
467 setattr(self,prm,value)
468 self.parameters.add(prm)
469
470 self.trace("parameter %s has been declared."%prm)
471
472 def releaseParameters(self,name):
473 """
474 Removes parameter name from the paramameters.
475 """
476 if self.isParameter(name):
477 self.parameters.remove(name)
478 self.trace("parameter %s has been removed."%name)
479
480 def checkLinkTargets(self, models, hash):
481 """
482 returns a set of tuples ("<self>(<name>)", <target model>) if the parameter <name> is linked to model <target model>
483 but <target model> is not in the list models. If the a parameter is linked to another parameter set which is not in the hash list
484 the parameter set is checked for its models. hash gives the call history.
485 """
486 out=set()
487 for name, value in self:
488 if isinstance(value, Link):
489 m=value.getTarget()
490 if isinstance(m, Model):
491 if not m in models: out.add( (str(self)+"("+name+")",m) )
492 elif isinstance(m, ParameterSet) and not m in hash:
493 out|=set( [ (str(self)+"("+name+")."+f[0],f[1]) for f in m.checkLinkTargets(models, hash+[ self ] ) ] )
494 return out
495
496 def __iter__(self):
497 """
498 Creates an iterator over the parameter and their values.
499 """
500 return _ParameterIterator(self)
501
502 def showParameters(self):
503 """
504 Returns a descrition of the parameters.
505 """
506 out="{"
507 notfirst=False
508 for i,v in self:
509 if notfirst: out=out+","
510 notfirst=True
511 if isinstance(v,ParameterSet):
512 out="%s\"%s\" : %s"%(out,i,v.showParameters())
513 else:
514 out="%s\"%s\" : %s"%(out,i,v)
515 return out+"}"
516
517 def __delattr__(self,name):
518 """
519 Removes the attribute name.
520 """
521 LinkableObject.__delattr__(self,name)
522 try:
523 self.releaseParameter(name)
524 except:
525 pass
526
527 def toDom(self, esysxml, node):
528 """
529 C{toDom} method of Model class
530 """
531 pset = esysxml.createElement('ParameterSet')
532 pset.setAttribute('type', self.__class__.__name__)
533 pset.setAttribute('module', self.__class__.__module__)
534 esysxml.registerLinkableObject(self, pset)
535 self._parametersToDom(esysxml, pset)
536 node.appendChild(pset)
537
538 def _parametersToDom(self, esysxml, node):
539 for name,value in self:
540 # convert list to numarray when possible:
541 if isinstance (value, list):
542 elem_type=-1
543 for i in value:
544 if isinstance(i, bool):
545 elem_type = max(elem_type,0)
546 if isinstance(i, int) and not isinstance(i, bool):
547 elem_type = max(elem_type,1)
548 if isinstance(i, float):
549 elem_type = max(elem_type,2)
550 if elem_type == 0: value = numarray.array(value,numarray.Bool)
551 if elem_type == 1: value = numarray.array(value,numarray.Int)
552 if elem_type == 2: value = numarray.array(value,numarray.Float)
553
554 param = esysxml.createElement('Parameter')
555 param.setAttribute('type', value.__class__.__name__)
556
557 param.appendChild(esysxml.createDataNode('Name', name))
558
559 val = esysxml.createElement('Value')
560 if isinstance(value,(ParameterSet,Link,DataSource)):
561 value.toDom(esysxml, val)
562 param.appendChild(val)
563 elif isinstance(value, numarray.NumArray):
564 shape = value.getshape()
565 if isinstance(shape, tuple):
566 size = reduce(operator.mul, shape)
567 shape = ' '.join(map(str, shape))
568 else:
569 size = shape
570 shape = str(shape)
571
572 arraytype = value.type()
573 if isinstance(arraytype, numarray.BooleanType):
574 arraytype_str="Bool"
575 elif isinstance(arraytype, numarray.IntegralType):
576 arraytype_str="Int"
577 elif isinstance(arraytype, numarray.FloatingType):
578 arraytype_str="Float"
579 elif isinstance(arraytype, numarray.ComplexType):
580 arraytype_str="Complex"
581 else:
582 arraytype_str=str(arraytype)
583 numarrayElement = esysxml.createElement('NumArray')
584 numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
585 numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
586 numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
587 [str(x) for x in numarray.reshape(value, size)])))
588 val.appendChild(numarrayElement)
589 param.appendChild(val)
590 elif isinstance(value, list):
591 param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
592 elif isinstance(value, (str, bool, int, float, type(None))):
593 param.appendChild(esysxml.createDataNode('Value', str(value)))
594 elif isinstance(value, dict):
595 dic = esysxml.createElement('dictionary')
596 if len(value.keys())>0:
597 dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
598 dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
599 for k,v in value.items():
600 i=esysxml.createElement('item')
601 i.appendChild(esysxml.createDataNode('key', k))
602 i.appendChild(esysxml.createDataNode('value', v))
603 dic.appendChild(i)
604 param.appendChild(dic)
605 else:
606 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
607
608 node.appendChild(param)
609
610 def fromDom(cls, esysxml, node):
611 # Define a host of helper functions to assist us.
612 def _children(node):
613 """
614 Remove the empty nodes from the children of this node.
615 """
616 ret = []
617 for x in node.childNodes:
618 if isinstance(x, minidom.Text):
619 if x.nodeValue.strip():
620 ret.append(x)
621 else:
622 ret.append(x)
623 return ret
624
625 def _floatfromValue(esysxml, node):
626 return float(node.nodeValue.strip())
627
628 def _stringfromValue(esysxml, node):
629 return str(node.nodeValue.strip())
630
631 def _intfromValue(esysxml, node):
632 return int(node.nodeValue.strip())
633
634 def _boolfromValue(esysxml, node):
635 return _boolfromstring(node.nodeValue.strip())
636
637 def _nonefromValue(esysxml, node):
638 return None
639
640 def _numarrayfromValue(esysxml, node):
641 for node in _children(node):
642 if node.tagName == 'ArrayType':
643 arraytype = node.firstChild.nodeValue.strip()
644 if node.tagName == 'Shape':
645 shape = node.firstChild.nodeValue.strip()
646 shape = [int(x) for x in shape.split()]
647 if node.tagName == 'Data':
648 data = node.firstChild.nodeValue.strip()
649 data = [float(x) for x in data.split()]
650 return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
651 shape)
652
653 def _listfromValue(esysxml, node):
654 return [x for x in node.nodeValue.split()]
655
656 def _boolfromstring(s):
657 if s == 'True':
658 return True
659 else:
660 return False
661 # Mapping from text types in the xml to methods used to process trees of that type
662 ptypemap = {"Simulation": Simulation.fromDom,
663 "Model":Model.fromDom,
664 "ParameterSet":ParameterSet.fromDom,
665 "Link":Link.fromDom,
666 "DataSource":DataSource.fromDom,
667 "float":_floatfromValue,
668 "int":_intfromValue,
669 "str":_stringfromValue,
670 "bool":_boolfromValue,
671 "list":_listfromValue,
672 "NumArray":_numarrayfromValue,
673 "NoneType":_nonefromValue,
674 }
675
676 parameters = {}
677 for n in _children(node):
678 ptype = n.getAttribute("type")
679 if not ptypemap.has_key(ptype):
680 raise KeyError("cannot handle parameter type %s."%ptype)
681
682 pname = pvalue = None
683 for childnode in _children(n):
684 if childnode.tagName == "Name":
685 pname = childnode.firstChild.nodeValue.strip()
686
687 if childnode.tagName == "Value":
688 nodes = _children(childnode)
689 pvalue = ptypemap[ptype](esysxml, nodes[0])
690
691 parameters[pname] = pvalue
692
693 # Create the instance of ParameterSet
694 o = cls(debug=esysxml.debug)
695 o.declareParameters(parameters)
696 esysxml.registerLinkableObject(o, node)
697 return o
698
699 fromDom = classmethod(fromDom)
700
701 def writeXML(self,ostream=stdout):
702 """
703 Writes the object as an XML object into an output stream.
704 """
705 esysxml=ESySXMLCreator()
706 self.toDom(esysxml, esysxml.getRoot())
707 ostream.write(esysxml.toprettyxml())
708
709 class Model(ParameterSet):
710 """
711 A Model object represents a processess marching over time until a
712 finalizing condition is fullfilled. At each time step an iterative
713 process can be performed and the time step size can be controlled. A
714 Model has the following work flow::
715
716 doInitialization()
717 while not terminateInitialIteration(): doInitializationiStep()
718 doInitialPostprocessing()
719 while not finalize():
720 dt=getSafeTimeStepSize(dt)
721 doStepPreprocessing(dt)
722 while not terminateIteration(): doStep(dt)
723 doStepPostprocessing(dt)
724 doFinalization()
725
726 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
727 C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
728 C{doFinalization} are methods of the particular instance of a Model. The
729 default implementations of these methods have to be overwritten by the
730 subclass implementing a Model.
731 """
732
733 UNDEF_DT=1.e300
734
735 def __init__(self,parameters=[],**kwargs):
736 """
737 Creates a model.
738
739 Just calls the parent constructor.
740 """
741 ParameterSet.__init__(self, parameters=parameters,**kwargs)
742
743 def __str__(self):
744 return "<%s %d>"%(self.__class__.__name__,id(self))
745
746
747 def doInitialization(self):
748 """
749 Initializes the time stepping scheme.
750
751 This function may be overwritten.
752 """
753 pass
754 def doInitialStep(self):
755 """
756 performs an iteration step in the initialization phase
757
758 This function may be overwritten.
759 """
760 pass
761
762 def terminateInitialIteration(self):
763 """
764 Returns True if iteration at the inital phase is terminated.
765 """
766 return True
767
768 def doInitialPostprocessing(self):
769 """
770 finalises the initialization iteration process
771
772 This function may be overwritten.
773 """
774 pass
775
776 def getSafeTimeStepSize(self,dt):
777 """
778 Returns a time step size which can safely be used.
779
780 C{dt} gives the previously used step size.
781
782 This function may be overwritten.
783 """
784 return self.UNDEF_DT
785
786 def finalize(self):
787 """
788 Returns False if the time stepping is finalized.
789
790 This function may be overwritten.
791 """
792 return False
793
794 def doFinalization(self):
795 """
796 Finalizes the time stepping.
797
798 This function may be overwritten.
799 """
800 pass
801
802 def doStepPreprocessing(self,dt):
803 """
804 Sets up a time step of step size dt.
805
806 This function may be overwritten.
807 """
808 pass
809
810 def doStep(self,dt):
811 """
812 Executes an iteration step at a time step.
813
814 C{dt} is the currently used time step size.
815
816 This function may be overwritten.
817 """
818 pass
819
820 def terminateIteration(self):
821 """
822 Returns True if iteration on a time step is terminated.
823 """
824 return True
825
826
827 def doStepPostprocessing(self,dt):
828 """
829 finalises the time step.
830
831 dt is the currently used time step size.
832
833 This function may be overwritten.
834 """
835 pass
836
837 def toDom(self, esysxml, node):
838 """
839 C{toDom} method of Model class
840 """
841 pset = esysxml.createElement('Model')
842 pset.setAttribute('type', self.__class__.__name__)
843 pset.setAttribute('module', self.__class__.__module__)
844 esysxml.registerLinkableObject(self, pset)
845 node.appendChild(pset)
846 self._parametersToDom(esysxml, pset)
847
848 class Simulation(Model):
849 """
850 A Simulation object is special Model which runs a sequence of Models.
851
852 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
853 C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
854 C{doFinalization} are executing the corresponding methods of the models in
855 the simulation.
856 """
857
858 FAILED_TIME_STEPS_MAX=20
859 MAX_ITER_STEPS=50
860 MAX_CHANGE_OF_DT=2.
861
862 def __init__(self, models=[], **kwargs):
863 """
864 Initiates a simulation from a list of models.
865 """
866 super(Simulation, self).__init__(**kwargs)
867 for m in models:
868 if not isinstance(m, Model):
869 raise TypeError("%s is not a subclass of Model."%m)
870 self.__models=[]
871 for i in range(len(models)):
872 self[i] = models[i]
873
874
875 def __repr__(self):
876 """
877 Returns a string representation of the Simulation.
878 """
879 return "<Simulation %r>" % self.__models
880
881 def __str__(self):
882 """
883 Returning Simulation as a string.
884 """
885 return "<Simulation %d>"%id(self)
886
887 def iterModels(self):
888 """
889 Returns an iterator over the models.
890 """
891 return self.__models
892
893 def __getitem__(self,i):
894 """
895 Returns the i-th model.
896 """
897 return self.__models[i]
898
899 def __setitem__(self,i,value):
900 """
901 Sets the i-th model.
902 """
903 if not isinstance(value,Model):
904 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
905 for j in range(max(i-len(self.__models)+1,0)):
906 self.__models.append(None)
907 self.__models[i]=value
908
909 def __len__(self):
910 """
911 Returns the number of models.
912 """
913 return len(self.__models)
914
915 def getAllModels(self):
916 """
917 returns a list of all models used in the Simulation including subsimulations
918 """
919 out=[]
920 for m in self.iterModels():
921 if isinstance(m, Simulation):
922 out+=m.getAllModels()
923 else:
924 out.append(m)
925 return list(set(out))
926
927 def checkModels(self, models, hash):
928 """
929 returns a list of (model,parameter, target model ) if the the parameter of model
930 is linking to the target_model which is not in list of models.
931 """
932 out=self.checkLinkTargets(models, hash + [self])
933 for m in self.iterModels():
934 if isinstance(m, Simulation):
935 out|=m.checkModels(models, hash)
936 else:
937 out|=m.checkLinkTargets(models, hash + [self])
938 return set( [ (str(self)+"."+f[0],f[1]) for f in out ] )
939
940
941 def getSafeTimeStepSize(self,dt):
942 """
943 Returns a time step size which can safely be used by all models.
944
945 This is the minimum over the time step sizes of all models.
946 """
947 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
948 return out
949
950 def doInitialization(self):
951 """
952 Initializes all models.
953 """
954 self.n=0
955 self.tn=0.
956 for o in self.iterModels():
957 o.doInitialization()
958 def doInitialStep(self):
959 """
960 performs an iteration step in the initialization step for all models
961 """
962 iter=0
963 while not self.terminateInitialIteration():
964 if iter==0: self.trace("iteration for initialization starts")
965 iter+=1
966 self.trace("iteration step %d"%(iter))
967 for o in self.iterModels():
968 o.doInitialStep()
969 if iter>self.MAX_ITER_STEPS:
970 raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
971 self.trace("Initialization finalized after %s iteration steps."%iter)
972
973 def doInitialPostprocessing(self):
974 """
975 finalises the initialization iteration process for all models.
976 """
977 for o in self.iterModels():
978 o.doInitialPostprocessing()
979 def finalize(self):
980 """
981 Returns True if any of the models is to be finalized.
982 """
983 return any([o.finalize() for o in self.iterModels()])
984
985 def doFinalization(self):
986 """
987 finalises the time stepping for all models.
988 """
989 for i in self.iterModels(): i.doFinalization()
990 self.trace("end of time integation.")
991
992 def doStepPreprocessing(self,dt):
993 """
994 Initializes the time step for all models.
995 """
996 for o in self.iterModels():
997 o.doStepPreprocessing(dt)
998
999 def terminateIteration(self):
1000 """
1001 Returns True if all iterations for all models are terminated.
1002 """
1003 out=all([o.terminateIteration() for o in self.iterModels()])
1004 return out
1005
1006 def terminateInitialIteration(self):
1007 """
1008 Returns True if all initial iterations for all models are terminated.
1009 """
1010 out=all([o.terminateInitialIteration() for o in self.iterModels()])
1011 return out
1012
1013 def doStepPostprocessing(self,dt):
1014 """
1015 finalises the iteration process for all models.
1016 """
1017 for o in self.iterModels():
1018 o.doStepPostprocessing(dt)
1019 self.n+=1
1020 self.tn+=dt
1021
1022 def doStep(self,dt):
1023 """
1024 Executes the iteration step at a time step for all model::
1025
1026 self.doStepPreprocessing(dt)
1027 while not self.terminateIteration():
1028 for all models:
1029 self.doStep(dt)
1030 self.doStepPostprocessing(dt)
1031 """
1032 self.iter=0
1033 while not self.terminateIteration():
1034 if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
1035 self.iter+=1
1036 self.trace("iteration step %d"%(self.iter))
1037 for o in self.iterModels():
1038 o.doStep(dt)
1039 if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
1040
1041 def run(self,check_point=None):
1042 """
1043 Run the simulation by performing essentially::
1044
1045 self.doInitialization()
1046 while not self.terminateInitialIteration(): self.doInitialStep()
1047 self.doInitialPostprocessing()
1048 while not self.finalize():
1049 dt=self.getSafeTimeStepSize()
1050 self.doStep(dt)
1051 if n%check_point==0:
1052 self.writeXML()
1053 self.doFinalization()
1054
1055 If one of the models in throws a C{FailedTimeStepError} exception a
1056 new time step size is computed through getSafeTimeStepSize() and the
1057 time step is repeated.
1058
1059 If one of the models in throws a C{IterationDivergenceError}
1060 exception the time step size is halved and the time step is repeated.
1061
1062 In both cases the time integration is given up after
1063 C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1064 """
1065 # check the completness of the models:
1066 # first a list of all the models involved in the simulation including subsimulations:
1067 #
1068 missing=self.checkModels(self.getAllModels(), [])
1069 if len(missing)>0:
1070 msg=""
1071 for l in missing:
1072 msg+="\n\t"+str(l[1])+" at "+l[0]
1073 raise MissingLink("link targets missing in the Simulation: %s"%msg)
1074 #==============================
1075 self.doInitialization()
1076 self.doInitialStep()
1077 self.doInitialPostprocessing()
1078 dt=self.UNDEF_DT
1079 while not self.finalize():
1080 step_fail_counter=0
1081 iteration_fail_counter=0
1082 if self.n==0:
1083 dt_new=self.getSafeTimeStepSize(dt)
1084 else:
1085 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1086 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1087 end_of_step=False
1088 while not end_of_step:
1089 end_of_step=True
1090 if not dt_new>0:
1091 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1092 try:
1093 self.doStepPreprocessing(dt_new)
1094 self.doStep(dt_new)
1095 self.doStepPostprocessing(dt_new)
1096 except IterationDivergenceError:
1097 dt_new*=0.5
1098 end_of_step=False
1099 iteration_fail_counter+=1
1100 if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1101 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1102 self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1103 except FailedTimeStepError:
1104 dt_new=self.getSafeTimeStepSize(dt)
1105 end_of_step=False
1106 step_fail_counter+=1
1107 self.trace("Time step is repeated with new time step size %s."%dt_new)
1108 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1109 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1110 dt=dt_new
1111 if not check_point==None:
1112 if n%check_point==0:
1113 self.trace("check point is created.")
1114 self.writeXML()
1115 self.doFinalization()
1116
1117
1118 def toDom(self, esysxml, node):
1119 """
1120 C{toDom} method of Simulation class.
1121 """
1122 simulation = esysxml.createElement('Simulation')
1123 esysxml.registerLinkableObject(self, simulation)
1124 for rank, sim in enumerate(self.iterModels()):
1125 component = esysxml.createElement('Component')
1126 component.setAttribute('rank', str(rank))
1127 sim.toDom(esysxml, component)
1128 simulation.appendChild(component)
1129 node.appendChild(simulation)
1130
1131
1132 def fromDom(cls, esysxml, node):
1133 sims = []
1134 for n in node.childNodes:
1135 if isinstance(n, minidom.Text):
1136 continue
1137 sims.append(esysxml.getComponent(n))
1138 sims.sort(cmp=_comp)
1139 sim=cls([s[1] for s in sims], debug=esysxml.debug)
1140 esysxml.registerLinkableObject(sim, node)
1141 return sim
1142
1143 fromDom = classmethod(fromDom)
1144
1145 def _comp(a,b):
1146 if a[0]<a[1]:
1147 return 1
1148 elif a[0]>a[1]:
1149 return -1
1150 else:
1151 return 0
1152
1153 class IterationDivergenceError(Exception):
1154 """
1155 Exception which is thrown if there is no convergence of the iteration
1156 process at a time step.
1157
1158 But there is a chance that a smaller step could help to reach convergence.
1159 """
1160 pass
1161
1162 class FailedTimeStepError(Exception):
1163 """
1164 Exception which is thrown if the time step fails because of a step
1165 size that have been choosen to be too large.
1166 """
1167 pass
1168
1169 class SimulationBreakDownError(Exception):
1170 """
1171 Exception which is thrown if the simulation does not manage to
1172 progress in time.
1173 """
1174 pass
1175
1176 class NonPositiveStepSizeError(Exception):
1177 """
1178 Exception which is thrown if the step size is not positive.
1179 """
1180 pass
1181
1182 class MissingLink(Exception):
1183 """
1184 Exception thrown when a link is missing
1185 """
1186 pass
1187
1188 class DataSource(object):
1189 """
1190 Class for handling data sources, including local and remote files. This class is under development.
1191 """
1192
1193 def __init__(self, uri="file.ext", fileformat="unknown"):
1194 self.uri = uri
1195 self.fileformat = fileformat
1196
1197 def toDom(self, esysxml, node):
1198 """
1199 C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1200 current XML esysxml.
1201 """
1202 ds = esysxml.createElement('DataSource')
1203 ds.appendChild(esysxml.createDataNode('URI', self.uri))
1204 ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1205 node.appendChild(ds)
1206
1207 def fromDom(cls, esysxml, node):
1208 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1209 fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1210 ds = cls(uri, fileformat)
1211 return ds
1212
1213 def getLocalFileName(self):
1214 return self.uri
1215
1216 fromDom = classmethod(fromDom)
1217
1218 # 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