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