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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 938 - (hide annotations)
Thu Jan 25 03:40:54 2007 UTC (12 years, 3 months ago) by gross
File MIME type: text/x-python
File size: 40142 byte(s)
the FinleyReade accepts now gmsh files (use format="gmsh")
and
Simulations are accepting Models only. Moreover, there is a test now
if a all Models targeted by a model in the simulation or a subsimulation
are included in  simulation or a subsimulation. 


1 jgs 121 # $Id$
2    
3 gross 637 """
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 elspeth 609 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
16     http://www.access.edu.au
17     Primary Business: Queensland, Australia"""
18 elspeth 614 __license__="""Licensed under the Open Software License version 3.0
19     http://www.opensource.org/licenses/osl-3.0.php"""
20 gross 637 __url__="http://www.iservo.edu.au/esys"
21     __version__="$Revision$"
22     __date__="$Date$"
23 elspeth 609
24 gross 637
25 jgs 122 from types import StringType,IntType,FloatType,BooleanType,ListType,DictType
26     from sys import stdout
27 elspeth 871 import numarray
28     import operator
29 jgs 123 import itertools
30 jgs 121
31 jgs 123 # 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 jgs 122 from xml.dom import minidom
38 jgs 121
39    
40 jgs 123 def all(seq):
41     for x in seq:
42     if not x:
43     return False
44     return True
45    
46 jgs 147 def any(seq):
47     for x in seq:
48     if x:
49     return True
50     return False
51    
52 jgs 150 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 gross 918 class ESySXMLParser(object):
67 jgs 147 """
68 gross 918 parser for ESysXML file
69     """
70     def __init__(self,xml, debug=False):
71     self.__dom = minidom.parseString(xml)
72     self.__linkable_object_registry= {}
73     self.__link_registry= []
74     self.__esys=self.__dom.firstChild
75     self.debug=debug
76    
77     def getClassPath(self, node):
78     type = node.getAttribute("type")
79     if (node.getAttribute("module")):
80     module = node.getAttribute("module")
81     return importName(module, type)
82     else:
83     return importName("__main__", type)
84 jgs 123
85 gross 918 def setLinks(self):
86     for obj_id, link in self.__link_registry:
87     link.target = self.__linkable_object_registry[obj_id]
88 jgs 123
89 gross 918 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 jgs 122 class Link:
198 jgs 123 """
199 jgs 149 A Link makes an attribute of an object callable::
200    
201 jgs 123 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 jgs 126 """
209 jgs 149 Creates a link to the object target. If attribute is given, the link is
210 jgs 123 establised to this attribute of the target. Otherwise the attribute is
211 jgs 126 undefined.
212     """
213 jgs 123 self.target = target
214     self.attribute = None
215     self.setAttributeName(attribute)
216 gross 912
217 gross 938 def getTarget(self):
218     """
219     returns the target
220     """
221     return self.target
222 gross 912 def getAttributeName(self):
223     """
224     returns the name of the attribute the link is pointing to
225     """
226     return self.attribute
227 jgs 123
228     def setAttributeName(self,attribute):
229 jgs 126 """
230 jgs 149 Set a new attribute name to be collected from the target object. The
231 jgs 126 target object must have the attribute with name attribute.
232     """
233 jgs 147 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 jgs 123 self.attribute = attribute
241    
242     def hasDefinedAttributeName(self):
243 jgs 126 """
244 jgs 149 Returns true if an attribute name is set.
245 jgs 126 """
246 jgs 123 return self.attribute != None
247    
248     def __repr__(self):
249 jgs 126 """
250 jgs 149 Returns a string representation of the link.
251 jgs 126 """
252 jgs 123 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 jgs 126 """
259 jgs 149 Returns the value of the attribute of the target object. If the
260 jgs 126 atrribute is callable then the return value of the call is returned.
261     """
262 jgs 123 if name:
263     out=getattr(self.target, name)
264     else:
265     out=getattr(self.target, self.attribute)
266 jgs 121
267 jgs 123 if callable(out):
268     return out()
269     else:
270     return out
271 jgs 121
272 gross 918 def toDom(self, esysxml, node):
273 jgs 126 """
274 jgs 149 C{toDom} method of Link. Creates a Link node and appends it to the
275 gross 918 current XML esysxml.
276 jgs 126 """
277 gross 918 link = esysxml.createElement('Link')
278 jgs 147 assert (self.target != None), ("Target was none, name was %r" % self.attribute)
279 gross 918 link.appendChild(esysxml.createDataNode('Target', esysxml.getLinkableObjectId(self.target)))
280 jgs 123 # 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 gross 918 link.appendChild(esysxml.createDataNode('Attribute', self.attribute))
284 jgs 123 node.appendChild(link)
285 jgs 121
286 gross 918 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 jgs 123 l = cls(None, attribute)
290 gross 918 esysxml.registerLink(targetid, l)
291 jgs 123 return l
292 jgs 121
293 jgs 123 fromDom = classmethod(fromDom)
294    
295 jgs 122 class LinkableObject(object):
296 jgs 123 """
297     An object that allows to link its attributes to attributes of other objects
298 jgs 149 via a Link object. For instance::
299 jgs 123
300     p = LinkableObject()
301     p.x = Link(o,"name")
302     print p.x
303    
304 jgs 149 links attribute C{x} of C{p} to the attribute name of object C{o}.
305 jgs 121
306 jgs 149 C{p.x} will contain the current value of attribute C{name} of object
307     C{o}.
308 jgs 121
309 jgs 149 If the value of C{getattr(o, "name")} is callable, C{p.x} will return
310     the return value of the call.
311 jgs 123 """
312    
313    
314 gross 918 def __init__(self, id = None, debug=False):
315 jgs 149 """
316     Initializes LinkableObject so that we can operate on Links
317     """
318 jgs 123 self.debug = debug
319     self.__linked_attributes={}
320 gross 918
321 jgs 123 def trace(self, msg):
322     """
323 jgs 149 If debugging is on, print the message, otherwise do nothing
324     """
325 jgs 123 if self.debug:
326 jgs 147 print "%s: %s"%(str(self),msg)
327 jgs 123
328     def __getattr__(self,name):
329 jgs 149 """
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 jgs 123 out = self.getAttributeObject(name)
334     if isinstance(out,Link):
335     return out()
336     else:
337     return out
338    
339     def getAttributeObject(self,name):
340 jgs 149 """
341     Return the object stored for attribute name.
342     """
343 jgs 123
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 jgs 147 if self.__class__.__dict__.has_key(name):
351     return self.__class.__dict__[name]
352    
353 jgs 123 raise AttributeError,"No attribute %s."%name
354    
355 jgs 147 def hasAttribute(self,name):
356 jgs 149 """
357     Returns True if self as attribute name.
358     """
359 jgs 147 return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name)
360    
361 jgs 123 def __setattr__(self,name,value):
362 jgs 149 """
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 jgs 123
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 jgs 147 self.trace("attribute %s is now linked by %s."%(name,value))
376 jgs 123 else:
377     self.__dict__[name] = value
378    
379     def __delattr__(self,name):
380 jgs 149 """
381     Removes the attribute name.
382     """
383 jgs 123
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 jgs 122 class _ParameterIterator:
392 jgs 123 def __init__(self,parameterset):
393 jgs 121
394 jgs 123 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 jgs 122 class ParameterSet(LinkableObject):
405 jgs 149 """
406     A class which allows to emphazise attributes to be written and read to XML
407 jgs 123
408 jgs 149 Leaves of an ESySParameters object can be:
409 jgs 123
410 jgs 149 - 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 elspeth 874 - a numarray object
418     - a list of booleans
419     - any other object (not considered by writeESySXML and writeXML)
420 jgs 123
421 jgs 149 Example how to create an ESySParameters object::
422 jgs 123
423 jgs 149 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 jgs 123
427 jgs 149 This can be accessed as::
428 jgs 123
429 jgs 149 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 jgs 123 """
439     def __init__(self, parameters=[], **kwargs):
440 jgs 149 """
441     Creates a ParameterSet with parameters parameters.
442     """
443 jgs 123 LinkableObject.__init__(self, **kwargs)
444     self.parameters = set()
445     self.declareParameters(parameters)
446 jgs 147
447     def __repr__(self):
448 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
449 jgs 123
450     def declareParameter(self,**parameters):
451 jgs 149 """
452     Declares a new parameter(s) and its (their) initial value.
453     """
454 jgs 123 self.declareParameters(parameters)
455    
456     def declareParameters(self,parameters):
457 jgs 149 """
458     Declares a set of parameters. parameters can be a list, a dictionary
459     or a ParameterSet.
460     """
461 jgs 123 if isinstance(parameters,ListType):
462     parameters = zip(parameters, itertools.repeat(None))
463     if isinstance(parameters,DictType):
464     parameters = parameters.iteritems()
465 jgs 121
466 jgs 123 for prm, value in parameters:
467     setattr(self,prm,value)
468     self.parameters.add(prm)
469 jgs 121
470 jgs 147 self.trace("parameter %s has been declared."%prm)
471 jgs 121
472 jgs 123 def releaseParameters(self,name):
473 jgs 149 """
474     Removes parameter name from the paramameters.
475     """
476 jgs 123 if self.isParameter(name):
477     self.parameters.remove(name)
478 jgs 147 self.trace("parameter %s has been removed."%name)
479 jgs 123
480     def __iter__(self):
481 jgs 149 """
482     Creates an iterator over the parameter and their values.
483     """
484 jgs 123 return _ParameterIterator(self)
485    
486     def showParameters(self):
487 jgs 149 """
488     Returns a descrition of the parameters.
489     """
490 jgs 123 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 jgs 149 """
503     Removes the attribute name.
504     """
505 jgs 123 LinkableObject.__delattr__(self,name)
506     try:
507     self.releaseParameter(name)
508     except:
509     pass
510 jgs 121
511 gross 918 def toDom(self, esysxml, node):
512 jgs 149 """
513 gross 918 C{toDom} method of Model class
514 jgs 149 """
515 gross 918 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 jgs 123 node.appendChild(pset)
521 jgs 121
522 gross 918 def _parametersToDom(self, esysxml, node):
523 jgs 123 for name,value in self:
524 gross 918 # 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 jgs 123 param.setAttribute('type', value.__class__.__name__)
540 jgs 121
541 gross 918 param.appendChild(esysxml.createDataNode('Name', name))
542 jgs 121
543 gross 918 val = esysxml.createElement('Value')
544 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
545 gross 918 value.toDom(esysxml, val)
546 jgs 123 param.appendChild(val)
547 elspeth 871 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 gross 918 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 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
572 elspeth 874 val.appendChild(numarrayElement)
573 elspeth 871 param.appendChild(val)
574 gross 918 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 gross 926 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 jgs 123 else:
590 gross 926 print value
591 gross 918 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
592 jgs 123
593     node.appendChild(param)
594    
595 gross 918 def fromDom(cls, esysxml, node):
596 jgs 123 # Define a host of helper functions to assist us.
597     def _children(node):
598     """
599 jgs 149 Remove the empty nodes from the children of this node.
600 jgs 123 """
601 elspeth 871 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 jgs 123
610 gross 918 def _floatfromValue(esysxml, node):
611     return float(node.nodeValue.strip())
612 jgs 123
613 gross 918 def _stringfromValue(esysxml, node):
614     return str(node.nodeValue.strip())
615 jgs 126
616 gross 918 def _intfromValue(esysxml, node):
617     return int(node.nodeValue.strip())
618 jgs 126
619 gross 918 def _boolfromValue(esysxml, node):
620     return _boolfromstring(node.nodeValue.strip())
621 elspeth 266
622 gross 918 def _nonefromValue(esysxml, node):
623 elspeth 266 return None
624 elspeth 871
625 gross 918 def _numarrayfromValue(esysxml, node):
626     for node in _children(node):
627 elspeth 871 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 elspeth 874
638 gross 918 def _listfromValue(esysxml, node):
639     return [x for x in node.nodeValue.split()]
640 elspeth 874
641     def _boolfromstring(s):
642     if s == 'True':
643     return True
644     else:
645     return False
646 jgs 123 # 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 elspeth 875 "DataSource":DataSource.fromDom,
652 jgs 123 "float":_floatfromValue,
653 jgs 126 "int":_intfromValue,
654 jgs 123 "str":_stringfromValue,
655 elspeth 269 "bool":_boolfromValue,
656 elspeth 874 "list":_listfromValue,
657     "NumArray":_numarrayfromValue,
658 elspeth 871 "NoneType":_nonefromValue,
659 jgs 123 }
660    
661     parameters = {}
662 gross 918 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 jgs 123
667     pname = pvalue = None
668 gross 918 for childnode in _children(n):
669 jgs 123 if childnode.tagName == "Name":
670     pname = childnode.firstChild.nodeValue.strip()
671    
672     if childnode.tagName == "Value":
673     nodes = _children(childnode)
674 gross 918 pvalue = ptypemap[ptype](esysxml, nodes[0])
675 jgs 123
676     parameters[pname] = pvalue
677    
678     # Create the instance of ParameterSet
679 gross 918 o = cls(debug=esysxml.debug)
680 jgs 123 o.declareParameters(parameters)
681 gross 918 esysxml.registerLinkableObject(o, node)
682 jgs 123 return o
683    
684     fromDom = classmethod(fromDom)
685 gross 918
686 jgs 123 def writeXML(self,ostream=stdout):
687 jgs 149 """
688     Writes the object as an XML object into an output stream.
689     """
690 gross 918 esysxml=ESySXMLCreator()
691     self.toDom(esysxml, esysxml.getRoot())
692     ostream.write(esysxml.toprettyxml())
693    
694 jgs 147 class Model(ParameterSet):
695     """
696 jgs 149 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 gross 906
701 jgs 147 doInitialization()
702 gross 906 while not terminateInitialIteration(): doInitializationiStep()
703     doInitialPostprocessing()
704 jgs 147 while not finalize():
705     dt=getSafeTimeStepSize(dt)
706     doStepPreprocessing(dt)
707     while not terminateIteration(): doStep(dt)
708     doStepPostprocessing(dt)
709     doFinalization()
710 jgs 121
711 jgs 149 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 jgs 147 """
717 jgs 121
718 jgs 147 UNDEF_DT=1.e300
719 jgs 121
720 gross 918 def __init__(self,parameters=[],**kwargs):
721 jgs 149 """
722     Creates a model.
723 jgs 121
724 jgs 149 Just calls the parent constructor.
725 jgs 147 """
726 gross 918 ParameterSet.__init__(self, parameters=parameters,**kwargs)
727 jgs 121
728 jgs 147 def __str__(self):
729 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
730 jgs 121
731    
732 jgs 147 def doInitialization(self):
733 jgs 149 """
734     Initializes the time stepping scheme.
735    
736     This function may be overwritten.
737     """
738 jgs 147 pass
739 gross 906 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 jgs 147
761     def getSafeTimeStepSize(self,dt):
762 jgs 149 """
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 jgs 147 return self.UNDEF_DT
770    
771     def finalize(self):
772 jgs 149 """
773     Returns False if the time stepping is finalized.
774    
775     This function may be overwritten.
776     """
777 jgs 147 return False
778 jgs 123
779 jgs 147 def doFinalization(self):
780 jgs 149 """
781     Finalizes the time stepping.
782    
783     This function may be overwritten.
784     """
785 jgs 147 pass
786    
787     def doStepPreprocessing(self,dt):
788 jgs 149 """
789     Sets up a time step of step size dt.
790    
791     This function may be overwritten.
792     """
793 jgs 147 pass
794    
795     def doStep(self,dt):
796 jgs 149 """
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 jgs 147 pass
804    
805     def terminateIteration(self):
806 jgs 149 """
807     Returns True if iteration on a time step is terminated.
808     """
809 jgs 147 return True
810 gross 906
811 jgs 147
812     def doStepPostprocessing(self,dt):
813 jgs 149 """
814 gross 906 finalises the time step.
815 jgs 149
816     dt is the currently used time step size.
817    
818     This function may be overwritten.
819     """
820 jgs 147 pass
821 gross 918
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 jgs 147
833     class Simulation(Model):
834     """
835 jgs 149 A Simulation object is special Model which runs a sequence of Models.
836 jgs 121
837 jgs 149 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 jgs 147 """
842    
843     FAILED_TIME_STEPS_MAX=20
844     MAX_ITER_STEPS=50
845 gross 836 MAX_CHANGE_OF_DT=2.
846 jgs 147
847     def __init__(self, models=[], **kwargs):
848 jgs 149 """
849     Initiates a simulation from a list of models.
850     """
851 gross 918 super(Simulation, self).__init__(**kwargs)
852 gross 938 for m in models:
853     if not isinstance(m, Model):
854     raise TypeError("%s is not a subclass of Model."%m)
855 jgs 147 self.__models=[]
856     for i in range(len(models)):
857     self[i] = models[i]
858 jgs 149
859 jgs 121
860 jgs 147 def __repr__(self):
861     """
862 jgs 149 Returns a string representation of the Simulation.
863 jgs 147 """
864     return "<Simulation %r>" % self.__models
865 jgs 121
866 jgs 147 def __str__(self):
867     """
868 jgs 149 Returning Simulation as a string.
869 jgs 147 """
870     return "<Simulation %d>"%id(self)
871    
872     def iterModels(self):
873 jgs 149 """
874     Returns an iterator over the models.
875     """
876 jgs 147 return self.__models
877    
878     def __getitem__(self,i):
879 jgs 149 """
880     Returns the i-th model.
881     """
882 jgs 147 return self.__models[i]
883    
884     def __setitem__(self,i,value):
885 jgs 149 """
886     Sets the i-th model.
887     """
888 jgs 147 if not isinstance(value,Model):
889 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
890 jgs 149 for j in range(max(i-len(self.__models)+1,0)):
891     self.__models.append(None)
892 jgs 147 self.__models[i]=value
893    
894     def __len__(self):
895 jgs 149 """
896     Returns the number of models.
897     """
898 jgs 147 return len(self.__models)
899 jgs 121
900 gross 938 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 jgs 147
929     def getSafeTimeStepSize(self,dt):
930 jgs 149 """
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 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
936     return out
937    
938     def doInitialization(self):
939 jgs 149 """
940     Initializes all models.
941     """
942 jgs 147 self.n=0
943     self.tn=0.
944     for o in self.iterModels():
945 gross 906 o.doInitialization()
946     def doInitialStep(self):
947     """
948     performs an iteration step in the initialization step for all models
949     """
950 gross 908 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 gross 906
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 jgs 147 def finalize(self):
968 jgs 149 """
969     Returns True if any of the models is to be finalized.
970     """
971 jgs 147 return any([o.finalize() for o in self.iterModels()])
972 jgs 123
973 jgs 147 def doFinalization(self):
974 jgs 149 """
975 gross 906 finalises the time stepping for all models.
976 jgs 149 """
977 jgs 147 for i in self.iterModels(): i.doFinalization()
978     self.trace("end of time integation.")
979    
980     def doStepPreprocessing(self,dt):
981 jgs 149 """
982     Initializes the time step for all models.
983     """
984 jgs 147 for o in self.iterModels():
985     o.doStepPreprocessing(dt)
986    
987     def terminateIteration(self):
988 jgs 149 """
989     Returns True if all iterations for all models are terminated.
990     """
991 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
992     return out
993 gross 906
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 jgs 123
1001 jgs 147 def doStepPostprocessing(self,dt):
1002 jgs 149 """
1003 gross 906 finalises the iteration process for all models.
1004 jgs 149 """
1005 jgs 147 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 jgs 149 Executes the iteration step at a time step for all model::
1013 jgs 147
1014 jgs 149 self.doStepPreprocessing(dt)
1015     while not self.terminateIteration():
1016     for all models:
1017     self.doStep(dt)
1018     self.doStepPostprocessing(dt)
1019 jgs 147 """
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 jgs 121
1029 jgs 147 def run(self,check_point=None):
1030     """
1031 jgs 149 Run the simulation by performing essentially::
1032 jgs 123
1033 jgs 149 self.doInitialization()
1034 gross 906 while not self.terminateInitialIteration(): self.doInitialStep()
1035     self.doInitialPostprocessing()
1036 jgs 149 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 jgs 121
1043 jgs 149 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 jgs 121
1047 jgs 149 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 jgs 121
1050 jgs 149 In both cases the time integration is given up after
1051     C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1052 jgs 147 """
1053 gross 938 # 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     line=str(self)
1061     settarget=False
1062     for c in l:
1063     if settarget:
1064     line+=" linked to "+str(c)
1065     else:
1066     line+="."+str(c)
1067     if isinstance(c,str): settarget=True
1068     msg+="\n\t"+line
1069     raise MissingLink("link targets missing in the Simulation: %s"%msg)
1070     #==============================
1071 gross 906 self.doInitialization()
1072 gross 908 self.doInitialStep()
1073 gross 906 self.doInitialPostprocessing()
1074 jgs 147 dt=self.UNDEF_DT
1075     while not self.finalize():
1076     step_fail_counter=0
1077     iteration_fail_counter=0
1078 gross 836 if self.n==0:
1079     dt_new=self.getSafeTimeStepSize(dt)
1080     else:
1081 gross 838 dt_new=min(max(self.getSafeTimeStepSize(dt),dt/self.MAX_CHANGE_OF_DT),dt*self.MAX_CHANGE_OF_DT)
1082 jgs 147 self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new))
1083     end_of_step=False
1084     while not end_of_step:
1085     end_of_step=True
1086     if not dt_new>0:
1087 gross 829 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.n+1))
1088 jgs 147 try:
1089     self.doStepPreprocessing(dt_new)
1090     self.doStep(dt_new)
1091     self.doStepPostprocessing(dt_new)
1092     except IterationDivergenceError:
1093     dt_new*=0.5
1094     end_of_step=False
1095     iteration_fail_counter+=1
1096     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1097 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1098     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1099 jgs 147 except FailedTimeStepError:
1100     dt_new=self.getSafeTimeStepSize(dt)
1101     end_of_step=False
1102     step_fail_counter+=1
1103 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1104 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1105 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1106 jgs 147 dt=dt_new
1107     if not check_point==None:
1108     if n%check_point==0:
1109     self.trace("check point is created.")
1110     self.writeXML()
1111     self.doFinalization()
1112 jgs 121
1113 gross 918
1114     def toDom(self, esysxml, node):
1115     """
1116     C{toDom} method of Simulation class.
1117     """
1118     simulation = esysxml.createElement('Simulation')
1119     esysxml.registerLinkableObject(self, simulation)
1120     for rank, sim in enumerate(self.iterModels()):
1121     component = esysxml.createElement('Component')
1122     component.setAttribute('rank', str(rank))
1123     sim.toDom(esysxml, component)
1124     simulation.appendChild(component)
1125     node.appendChild(simulation)
1126    
1127    
1128     def fromDom(cls, esysxml, node):
1129 jgs 147 sims = []
1130 gross 918 for n in node.childNodes:
1131     if isinstance(n, minidom.Text):
1132 jgs 147 continue
1133 gross 918 sims.append(esysxml.getComponent(n))
1134     sims.sort(cmp=_comp)
1135     sim=cls([s[1] for s in sims], debug=esysxml.debug)
1136     esysxml.registerLinkableObject(sim, node)
1137     return sim
1138 jgs 121
1139 jgs 147 fromDom = classmethod(fromDom)
1140 jgs 121
1141 gross 918 def _comp(a,b):
1142     if a[0]<a[1]:
1143     return 1
1144     elif a[0]>a[1]:
1145     return -1
1146     else:
1147     return 0
1148 jgs 121
1149 jgs 147 class IterationDivergenceError(Exception):
1150     """
1151 jgs 149 Exception which is thrown if there is no convergence of the iteration
1152     process at a time step.
1153 jgs 121
1154 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1155 jgs 147 """
1156     pass
1157 jgs 121
1158 jgs 147 class FailedTimeStepError(Exception):
1159 jgs 149 """
1160     Exception which is thrown if the time step fails because of a step
1161     size that have been choosen to be too large.
1162     """
1163 jgs 147 pass
1164 jgs 121
1165 jgs 147 class SimulationBreakDownError(Exception):
1166 jgs 149 """
1167     Exception which is thrown if the simulation does not manage to
1168     progress in time.
1169     """
1170 jgs 147 pass
1171 jgs 121
1172 jgs 147 class NonPositiveStepSizeError(Exception):
1173 jgs 149 """
1174     Exception which is thrown if the step size is not positive.
1175     """
1176 jgs 147 pass
1177 jgs 149
1178 gross 938 class MissingLink(Exception):
1179     """
1180     Exception thrown when a link is missing
1181     """
1182     pass
1183    
1184 elspeth 875 class DataSource(object):
1185     """
1186     Class for handling data sources, including local and remote files. This class is under development.
1187     """
1188    
1189 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1190 elspeth 875 self.uri = uri
1191     self.fileformat = fileformat
1192    
1193 gross 918 def toDom(self, esysxml, node):
1194 elspeth 875 """
1195     C{toDom} method of DataSource. Creates a DataSource node and appends it to the
1196 gross 918 current XML esysxml.
1197 elspeth 875 """
1198 gross 918 ds = esysxml.createElement('DataSource')
1199     ds.appendChild(esysxml.createDataNode('URI', self.uri))
1200     ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1201 elspeth 875 node.appendChild(ds)
1202    
1203 gross 918 def fromDom(cls, esysxml, node):
1204 gross 920 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1205     fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1206 elspeth 875 ds = cls(uri, fileformat)
1207     return ds
1208    
1209 gross 885 def getLocalFileName(self):
1210     return self.uri
1211    
1212 elspeth 875 fromDom = classmethod(fromDom)
1213    
1214 jgs 149 # 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