/[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 954 - (show annotations)
Fri Feb 9 08:56:14 2007 UTC (12 years, 9 months ago) by gross
File MIME type: text/x-python
File size: 40587 byte(s)
testing for completness if simulations 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 def releaseParameters(self,name):
471 """
472 Removes parameter name from the paramameters.
473 """
474 if self.isParameter(name):
475 self.parameters.remove(name)
476 self.trace("parameter %s has been removed."%name)
477
478 def checkLinkTargets(self, models, hash):
479 """
480 returns a set of tuples ("<self>(<name>)", <target model>) if the parameter <name> is linked to model <target model>
481 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
482 the parameter set is checked for its models. hash gives the call history.
483 """
484 out=set()
485 for name, value in self:
486 if isinstance(value, Link):
487 m=value.getTarget()
488 if isinstance(m, Model):
489 if not m in models: out.add( (str(self)+"("+name+")",m) )
490 elif isinstance(m, ParameterSet) and not m in hash:
491 out|=set( [ (str(self)+"("+name+")."+f[0],f[1]) for f in m.checkLinkTargets(models, hash+[ self ] ) ] )
492 return out
493
494 def __iter__(self):
495 """
496 Creates an iterator over the parameter and their values.
497 """
498 return _ParameterIterator(self)
499
500 def showParameters(self):
501 """
502 Returns a descrition of the parameters.
503 """
504 out="{"
505 notfirst=False
506 for i,v in self:
507 if notfirst: out=out+","
508 notfirst=True
509 if isinstance(v,ParameterSet):
510 out="%s\"%s\" : %s"%(out,i,v.showParameters())
511 else:
512 out="%s\"%s\" : %s"%(out,i,v)
513 return out+"}"
514
515 def __delattr__(self,name):
516 """
517 Removes the attribute name.
518 """
519 LinkableObject.__delattr__(self,name)
520 try:
521 self.releaseParameter(name)
522 except:
523 pass
524
525 def toDom(self, esysxml, node):
526 """
527 C{toDom} method of Model class
528 """
529 pset = esysxml.createElement('ParameterSet')
530 pset.setAttribute('type', self.__class__.__name__)
531 pset.setAttribute('module', self.__class__.__module__)
532 esysxml.registerLinkableObject(self, pset)
533 self._parametersToDom(esysxml, pset)
534 node.appendChild(pset)
535
536 def _parametersToDom(self, esysxml, node):
537 for name,value in self:
538 # convert list to numarray when possible:
539 if isinstance (value, list):
540 elem_type=-1
541 for i in value:
542 if isinstance(i, bool):
543 elem_type = max(elem_type,0)
544 if isinstance(i, int) and not isinstance(i, bool):
545 elem_type = max(elem_type,1)
546 if isinstance(i, float):
547 elem_type = max(elem_type,2)
548 if elem_type == 0: value = numarray.array(value,numarray.Bool)
549 if elem_type == 1: value = numarray.array(value,numarray.Int)
550 if elem_type == 2: value = numarray.array(value,numarray.Float)
551
552 param = esysxml.createElement('Parameter')
553 param.setAttribute('type', value.__class__.__name__)
554
555 param.appendChild(esysxml.createDataNode('Name', name))
556
557 val = esysxml.createElement('Value')
558 if isinstance(value,(ParameterSet,Link,DataSource)):
559 value.toDom(esysxml, val)
560 param.appendChild(val)
561 elif isinstance(value, numarray.NumArray):
562 shape = value.getshape()
563 if isinstance(shape, tuple):
564 size = reduce(operator.mul, shape)
565 shape = ' '.join(map(str, shape))
566 else:
567 size = shape
568 shape = str(shape)
569
570 arraytype = value.type()
571 if isinstance(arraytype, numarray.BooleanType):
572 arraytype_str="Bool"
573 elif isinstance(arraytype, numarray.IntegralType):
574 arraytype_str="Int"
575 elif isinstance(arraytype, numarray.FloatingType):
576 arraytype_str="Float"
577 elif isinstance(arraytype, numarray.ComplexType):
578 arraytype_str="Complex"
579 else:
580 arraytype_str=str(arraytype)
581 numarrayElement = esysxml.createElement('NumArray')
582 numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
583 numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
584 numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
585 [str(x) for x in numarray.reshape(value, size)])))
586 val.appendChild(numarrayElement)
587 param.appendChild(val)
588 elif isinstance(value, list):
589 param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
590 elif isinstance(value, (str, bool, int, float, type(None))):
591 param.appendChild(esysxml.createDataNode('Value', str(value)))
592 elif isinstance(value, dict):
593 dic = esysxml.createElement('dictionary')
594 if len(value.keys())>0:
595 dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
596 dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
597 for k,v in value.items():
598 i=esysxml.createElement('item')
599 i.appendChild(esysxml.createDataNode('key', k))
600 i.appendChild(esysxml.createDataNode('value', v))
601 dic.appendChild(i)
602 param.appendChild(dic)
603 else:
604 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
605
606 node.appendChild(param)
607
608 def fromDom(cls, esysxml, node):
609 # Define a host of helper functions to assist us.
610 def _children(node):
611 """
612 Remove the empty nodes from the children of this node.
613 """
614 ret = []
615 for x in node.childNodes:
616 if isinstance(x, minidom.Text):
617 if x.nodeValue.strip():
618 ret.append(x)
619 else:
620 ret.append(x)
621 return ret
622
623 def _floatfromValue(esysxml, node):
624 return float(node.nodeValue.strip())
625
626 def _stringfromValue(esysxml, node):
627 return str(node.nodeValue.strip())
628
629 def _intfromValue(esysxml, node):
630 return int(node.nodeValue.strip())
631
632 def _boolfromValue(esysxml, node):
633 return _boolfromstring(node.nodeValue.strip())
634
635 def _nonefromValue(esysxml, node):
636 return None
637
638 def _numarrayfromValue(esysxml, node):
639 for node in _children(node):
640 if node.tagName == 'ArrayType':
641 arraytype = node.firstChild.nodeValue.strip()
642 if node.tagName == 'Shape':
643 shape = node.firstChild.nodeValue.strip()
644 shape = [int(x) for x in shape.split()]
645 if node.tagName == 'Data':
646 data = node.firstChild.nodeValue.strip()
647 data = [float(x) for x in data.split()]
648 return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
649 shape)
650
651 def _listfromValue(esysxml, node):
652 return [x for x in node.nodeValue.split()]
653
654 def _boolfromstring(s):
655 if s == 'True':
656 return True
657 else:
658 return False
659 # Mapping from text types in the xml to methods used to process trees of that type
660 ptypemap = {"Simulation": Simulation.fromDom,
661 "Model":Model.fromDom,
662 "ParameterSet":ParameterSet.fromDom,
663 "Link":Link.fromDom,
664 "DataSource":DataSource.fromDom,
665 "float":_floatfromValue,
666 "int":_intfromValue,
667 "str":_stringfromValue,
668 "bool":_boolfromValue,
669 "list":_listfromValue,
670 "NumArray":_numarrayfromValue,
671 "NoneType":_nonefromValue,
672 }
673
674 parameters = {}
675 for n in _children(node):
676 ptype = n.getAttribute("type")
677 if not ptypemap.has_key(ptype):
678 raise KeyError("cannot handle parameter type %s."%ptype)
679
680 pname = pvalue = None
681 for childnode in _children(n):
682 if childnode.tagName == "Name":
683 pname = childnode.firstChild.nodeValue.strip()
684
685 if childnode.tagName == "Value":
686 nodes = _children(childnode)
687 pvalue = ptypemap[ptype](esysxml, nodes[0])
688
689 parameters[pname] = pvalue
690
691 # Create the instance of ParameterSet
692 o = cls(debug=esysxml.debug)
693 o.declareParameters(parameters)
694 esysxml.registerLinkableObject(o, node)
695 return o
696
697 fromDom = classmethod(fromDom)
698
699 def writeXML(self,ostream=stdout):
700 """
701 Writes the object as an XML object into an output stream.
702 """
703 esysxml=ESySXMLCreator()
704 self.toDom(esysxml, esysxml.getRoot())
705 ostream.write(esysxml.toprettyxml())
706
707 class Model(ParameterSet):
708 """
709 A Model object represents a processess marching over time until a
710 finalizing condition is fullfilled. At each time step an iterative
711 process can be performed and the time step size can be controlled. A
712 Model has the following work flow::
713
714 doInitialization()
715 while not terminateInitialIteration(): doInitializationiStep()
716 doInitialPostprocessing()
717 while not finalize():
718 dt=getSafeTimeStepSize(dt)
719 doStepPreprocessing(dt)
720 while not terminateIteration(): doStep(dt)
721 doStepPostprocessing(dt)
722 doFinalization()
723
724 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
725 C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
726 C{doFinalization} are methods of the particular instance of a Model. The
727 default implementations of these methods have to be overwritten by the
728 subclass implementing a Model.
729 """
730
731 UNDEF_DT=1.e300
732
733 def __init__(self,parameters=[],**kwargs):
734 """
735 Creates a model.
736
737 Just calls the parent constructor.
738 """
739 ParameterSet.__init__(self, parameters=parameters,**kwargs)
740
741 def __str__(self):
742 return "<%s %d>"%(self.__class__.__name__,id(self))
743
744
745 def doInitialization(self):
746 """
747 Initializes the time stepping scheme.
748
749 This function may be overwritten.
750 """
751 pass
752 def doInitialStep(self):
753 """
754 performs an iteration step in the initialization phase
755
756 This function may be overwritten.
757 """
758 pass
759
760 def terminateInitialIteration(self):
761 """
762 Returns True if iteration at the inital phase is terminated.
763 """
764 return True
765
766 def doInitialPostprocessing(self):
767 """
768 finalises the initialization iteration process
769
770 This function may be overwritten.
771 """
772 pass
773
774 def getSafeTimeStepSize(self,dt):
775 """
776 Returns a time step size which can safely be used.
777
778 C{dt} gives the previously used step size.
779
780 This function may be overwritten.
781 """
782 return self.UNDEF_DT
783
784 def finalize(self):
785 """
786 Returns False if the time stepping is finalized.
787
788 This function may be overwritten.
789 """
790 return False
791
792 def doFinalization(self):
793 """
794 Finalizes the time stepping.
795
796 This function may be overwritten.
797 """
798 pass
799
800 def doStepPreprocessing(self,dt):
801 """
802 Sets up a time step of step size dt.
803
804 This function may be overwritten.
805 """
806 pass
807
808 def doStep(self,dt):
809 """
810 Executes an iteration step at a time step.
811
812 C{dt} is the currently used time step size.
813
814 This function may be overwritten.
815 """
816 pass
817
818 def terminateIteration(self):
819 """
820 Returns True if iteration on a time step is terminated.
821 """
822 return True
823
824
825 def doStepPostprocessing(self,dt):
826 """
827 finalises the time step.
828
829 dt is the currently used time step size.
830
831 This function may be overwritten.
832 """
833 pass
834
835 def toDom(self, esysxml, node):
836 """
837 C{toDom} method of Model class
838 """
839 pset = esysxml.createElement('Model')
840 pset.setAttribute('type', self.__class__.__name__)
841 pset.setAttribute('module', self.__class__.__module__)
842 esysxml.registerLinkableObject(self, pset)
843 node.appendChild(pset)
844 self._parametersToDom(esysxml, pset)
845
846 class Simulation(Model):
847 """
848 A Simulation object is special Model which runs a sequence of Models.
849
850 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
851 C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
852 C{doFinalization} are executing the corresponding methods of the models in
853 the simulation.
854 """
855
856 FAILED_TIME_STEPS_MAX=20
857 MAX_ITER_STEPS=50
858 MAX_CHANGE_OF_DT=2.
859
860 def __init__(self, models=[], **kwargs):
861 """
862 Initiates a simulation from a list of models.
863 """
864 super(Simulation, self).__init__(**kwargs)
865 for m in models:
866 if not isinstance(m, Model):
867 raise TypeError("%s is not a subclass of Model."%m)
868 self.__models=[]
869 for i in range(len(models)):
870 self[i] = models[i]
871
872
873 def __repr__(self):
874 """
875 Returns a string representation of the Simulation.
876 """
877 return "<Simulation %r>" % self.__models
878
879 def __str__(self):
880 """
881 Returning Simulation as a string.
882 """
883 return "<Simulation %d>"%id(self)
884
885 def iterModels(self):
886 """
887 Returns an iterator over the models.
888 """
889 return self.__models
890
891 def __getitem__(self,i):
892 """
893 Returns the i-th model.
894 """
895 return self.__models[i]
896
897 def __setitem__(self,i,value):
898 """
899 Sets the i-th model.
900 """
901 if not isinstance(value,Model):
902 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
903 for j in range(max(i-len(self.__models)+1,0)):
904 self.__models.append(None)
905 self.__models[i]=value
906
907 def __len__(self):
908 """
909 Returns the number of models.
910 """
911 return len(self.__models)
912
913 def getAllModels(self):
914 """
915 returns a list of all models used in the Simulation including subsimulations
916 """
917 out=[]
918 for m in self.iterModels():
919 if isinstance(m, Simulation):
920 out+=m.getAllModels()
921 else:
922 out.append(m)
923 return list(set(out))
924
925 def checkModels(self, models, hash):
926 """
927 returns a list of (model,parameter, target model ) if the the parameter of model
928 is linking to the target_model which is not in list of models.
929 """
930 out=self.checkLinkTargets(models, hash + [self])
931 for m in self.iterModels():
932 if isinstance(m, Simulation):
933 out|=m.checkModels(models, hash)
934 else:
935 out|=m.checkLinkTargets(models, hash + [self])
936 return set( [ (str(self)+"."+f[0],f[1]) for f in out ] )
937
938
939 def getSafeTimeStepSize(self,dt):
940 """
941 Returns a time step size which can safely be used by all models.
942
943 This is the minimum over the time step sizes of all models.
944 """
945 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
946 return out
947
948 def doInitialization(self):
949 """
950 Initializes all models.
951 """
952 self.n=0
953 self.tn=0.
954 for o in self.iterModels():
955 o.doInitialization()
956 def doInitialStep(self):
957 """
958 performs an iteration step in the initialization step for all models
959 """
960 iter=0
961 while not self.terminateInitialIteration():
962 if iter==0: self.trace("iteration for initialization starts")
963 iter+=1
964 self.trace("iteration step %d"%(iter))
965 for o in self.iterModels():
966 o.doInitialStep()
967 if iter>self.MAX_ITER_STEPS:
968 raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
969 self.trace("Initialization finalized after %s iteration steps."%iter)
970
971 def doInitialPostprocessing(self):
972 """
973 finalises the initialization iteration process for all models.
974 """
975 for o in self.iterModels():
976 o.doInitialPostprocessing()
977 def finalize(self):
978 """
979 Returns True if any of the models is to be finalized.
980 """
981 return any([o.finalize() for o in self.iterModels()])
982
983 def doFinalization(self):
984 """
985 finalises the time stepping for all models.
986 """
987 for i in self.iterModels(): i.doFinalization()
988 self.trace("end of time integation.")
989
990 def doStepPreprocessing(self,dt):
991 """
992 Initializes the time step for all models.
993 """
994 for o in self.iterModels():
995 o.doStepPreprocessing(dt)
996
997 def terminateIteration(self):
998 """
999 Returns True if all iterations for all models are terminated.
1000 """
1001 out=all([o.terminateIteration() for o in self.iterModels()])
1002 return out
1003
1004 def terminateInitialIteration(self):
1005 """
1006 Returns True if all initial iterations for all models are terminated.
1007 """
1008 out=all([o.terminateInitialIteration() for o in self.iterModels()])
1009 return out
1010
1011 def doStepPostprocessing(self,dt):
1012 """
1013 finalises the iteration process for all models.
1014 """
1015 for o in self.iterModels():
1016 o.doStepPostprocessing(dt)
1017 self.n+=1
1018 self.tn+=dt
1019
1020 def doStep(self,dt):
1021 """
1022 Executes the iteration step at a time step for all model::
1023
1024 self.doStepPreprocessing(dt)
1025 while not self.terminateIteration():
1026 for all models:
1027 self.doStep(dt)
1028 self.doStepPostprocessing(dt)
1029 """
1030 self.iter=0
1031 while not self.terminateIteration():
1032 if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt))
1033 self.iter+=1
1034 self.trace("iteration step %d"%(self.iter))
1035 for o in self.iterModels():
1036 o.doStep(dt)
1037 if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt))
1038
1039 def run(self,check_point=None):
1040 """
1041 Run the simulation by performing essentially::
1042
1043 self.doInitialization()
1044 while not self.terminateInitialIteration(): self.doInitialStep()
1045 self.doInitialPostprocessing()
1046 while not self.finalize():
1047 dt=self.getSafeTimeStepSize()
1048 self.doStep(dt)
1049 if n%check_point==0:
1050 self.writeXML()
1051 self.doFinalization()
1052
1053 If one of the models in throws a C{FailedTimeStepError} exception a
1054 new time step size is computed through getSafeTimeStepSize() and the
1055 time step is repeated.
1056
1057 If one of the models in throws a C{IterationDivergenceError}
1058 exception the time step size is halved and the time step is repeated.
1059
1060 In both cases the time integration is given up after
1061 C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1062 """
1063 # check the completness of the models:
1064 # first a list of all the models involved in the simulation including subsimulations:
1065 #
1066 missing=self.checkModels(self.getAllModels(), [])
1067 if len(missing)>0:
1068 msg=""
1069 for l in missing:
1070 msg+="\n\t"+str(l[1])+" at "+l[0]
1071 raise MissingLink("link targets missing in the Simulation: %s"%msg)
1072 #==============================
1073 self.doInitialization()
1074 self.doInitialStep()
1075 self.doInitialPostprocessing()
1076 dt=self.UNDEF_DT
1077 while not self.finalize():
1078 step_fail_counter=0
1079 iteration_fail_counter=0
1080 if self.n==0:
1081 dt_new=self.getSafeTimeStepSize(dt)
1082 else:
1083 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1084 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1085 end_of_step=False
1086 while not end_of_step:
1087 end_of_step=True
1088 if not dt_new>0:
1089 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1090 try:
1091 self.doStepPreprocessing(dt_new)
1092 self.doStep(dt_new)
1093 self.doStepPostprocessing(dt_new)
1094 except IterationDivergenceError:
1095 dt_new*=0.5
1096 end_of_step=False
1097 iteration_fail_counter+=1
1098 if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1099 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1100 self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1101 except FailedTimeStepError:
1102 dt_new=self.getSafeTimeStepSize(dt)
1103 end_of_step=False
1104 step_fail_counter+=1
1105 self.trace("Time step is repeated with new time step size %s."%dt_new)
1106 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1107 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1108 dt=dt_new
1109 if not check_point==None:
1110 if n%check_point==0:
1111 self.trace("check point is created.")
1112 self.writeXML()
1113 self.doFinalization()
1114
1115
1116 def toDom(self, esysxml, node):
1117 """
1118 C{toDom} method of Simulation class.
1119 """
1120 simulation = esysxml.createElement('Simulation')
1121 esysxml.registerLinkableObject(self, simulation)
1122 for rank, sim in enumerate(self.iterModels()):
1123 component = esysxml.createElement('Component')
1124 component.setAttribute('rank', str(rank))
1125 sim.toDom(esysxml, component)
1126 simulation.appendChild(component)
1127 node.appendChild(simulation)
1128
1129
1130 def fromDom(cls, esysxml, node):
1131 sims = []
1132 for n in node.childNodes:
1133 if isinstance(n, minidom.Text):
1134 continue
1135 sims.append(esysxml.getComponent(n))
1136 sims.sort(cmp=_comp)
1137 sim=cls([s[1] for s in sims], debug=esysxml.debug)
1138 esysxml.registerLinkableObject(sim, node)
1139 return sim
1140
1141 fromDom = classmethod(fromDom)
1142
1143 def _comp(a,b):
1144 if a[0]<a[1]:
1145 return 1
1146 elif a[0]>a[1]:
1147 return -1
1148 else:
1149 return 0
1150
1151 class IterationDivergenceError(Exception):
1152 """
1153 Exception which is thrown if there is no convergence of the iteration
1154 process at a time step.
1155
1156 But there is a chance that a smaller step could help to reach convergence.
1157 """
1158 pass
1159
1160 class FailedTimeStepError(Exception):
1161 """
1162 Exception which is thrown if the time step fails because of a step
1163 size that have been choosen to be too large.
1164 """
1165 pass
1166
1167 class SimulationBreakDownError(Exception):
1168 """
1169 Exception which is thrown if the simulation does not manage to
1170 progress in time.
1171 """
1172 pass
1173
1174 class NonPositiveStepSizeError(Exception):
1175 """
1176 Exception which is thrown if the step size is not positive.
1177 """
1178 pass
1179
1180 class MissingLink(Exception):
1181 """
1182 Exception thrown when a link is missing
1183 """
1184 pass
1185
1186 class DataSource(object):
1187 """
1188 Class for handling data sources, including local and remote files. This class is under development.
1189 """
1190
1191 def __init__(self, uri="file.ext", fileformat="unknown"):
1192 self.uri = uri
1193 self.fileformat = fileformat
1194
1195 def toDom(self, esysxml, node):
1196 """
1197 C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1198 current XML esysxml.
1199 """
1200 ds = esysxml.createElement('DataSource')
1201 ds.appendChild(esysxml.createDataNode('URI', self.uri))
1202 ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1203 node.appendChild(ds)
1204
1205 def fromDom(cls, esysxml, node):
1206 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1207 fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1208 ds = cls(uri, fileformat)
1209 return ds
1210
1211 def getLocalFileName(self):
1212 return self.uri
1213
1214 fromDom = classmethod(fromDom)
1215
1216 # 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