/[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 2344 - (hide annotations)
Mon Mar 30 02:13:58 2009 UTC (10 years, 6 months ago) by jfenwick
File MIME type: text/x-python
File size: 47365 byte(s)
Change __url__ to launchpad site

1 ksteube 1809
2     ########################################################
3 ksteube 1312 #
4 ksteube 1809 # Copyright (c) 2003-2008 by University of Queensland
5     # Earth Systems Science Computational Center (ESSCC)
6     # http://www.uq.edu.au/esscc
7 ksteube 1312 #
8 ksteube 1809 # Primary Business: Queensland, Australia
9     # Licensed under the Open Software License version 3.0
10     # http://www.opensource.org/licenses/osl-3.0.php
11 ksteube 1312 #
12 ksteube 1809 ########################################################
13 jgs 121
14 ksteube 1809 __copyright__="""Copyright (c) 2003-2008 by University of Queensland
15     Earth Systems Science Computational Center (ESSCC)
16     http://www.uq.edu.au/esscc
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 jfenwick 2344 __url__="https://launchpad.net/escript-finley"
21 ksteube 1809
22 gross 637 """
23 caltinay 2169 Environment for implementing models in escript
24 gross 637
25     @var __author__: name of author
26     @var __copyright__: copyrights
27     @var __license__: licence agreement
28     @var __url__: url entry point on documentation
29     @var __version__: version
30     @var __date__: date of the version
31     """
32    
33     __author__="Lutz Gross, l.gross@uq.edu.au"
34 elspeth 609
35 gross 637
36 jgs 122 from types import StringType,IntType,FloatType,BooleanType,ListType,DictType
37     from sys import stdout
38 elspeth 871 import numarray
39     import operator
40 jgs 123 import itertools
41 gross 975 import time
42     import os
43 jgs 121
44 jgs 123 # import the 'set' module if it's not defined (python2.3/2.4 difference)
45     try:
46     set
47     except NameError:
48     from sets import Set as set
49    
50 jgs 122 from xml.dom import minidom
51 jgs 121
52    
53 jgs 123 def all(seq):
54 caltinay 2169 """
55     Returns True if no element in C{seq} is C{None}, False otherwise.
56     """
57 jgs 123 for x in seq:
58     if not x:
59     return False
60     return True
61    
62 jgs 147 def any(seq):
63 caltinay 2169 """
64     Returns True if not all elements in C{seq} are C{None}, False otherwise.
65     """
66 jgs 147 for x in seq:
67     if x:
68     return True
69     return False
70    
71 jgs 150 def importName(modulename, name):
72 caltinay 2169 """
73     Imports a named object from a module in the context of this function,
74     which means you should use fully qualified module paths.
75     Returns None on failure.
76 jgs 150
77 caltinay 2169 This function is from U{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52241}
78 jgs 150 """
79     module = __import__(modulename, globals(), locals(), [name])
80 caltinay 2169
81 jgs 150 try:
82     return vars(module)[name]
83     except KeyError:
84     raise ImportError("Could not import %s from %s" % (name, modulename))
85    
86 gross 918 class ESySXMLParser(object):
87 jgs 147 """
88 caltinay 2169 Parser for an ESysXML file.
89 gross 918 """
90     def __init__(self,xml, debug=False):
91 caltinay 2169 self.__dom = minidom.parseString(xml)
92     self.__linkable_object_registry= {}
93     self.__link_registry= []
94     self.__esys=self.__dom.getElementsByTagName('ESys')[0]
95     self.debug=debug
96    
97     def getClassPath(self, node):
98 gross 918 type = node.getAttribute("type")
99     if (node.getAttribute("module")):
100     module = node.getAttribute("module")
101     return importName(module, type)
102     else:
103     return importName("__main__", type)
104 jgs 123
105 gross 918 def setLinks(self):
106 caltinay 2169 for obj_id, link in self.__link_registry:
107 gross 918 link.target = self.__linkable_object_registry[obj_id]
108 jgs 123
109 gross 918 def parse(self):
110     """
111 caltinay 2169 Parses EsysXML and returns the list of generating ParameterSets.
112 gross 918 """
113     found=[]
114     for node in self.__esys.childNodes:
115     if isinstance(node, minidom.Element):
116     if node.tagName == 'Simulation':
117 caltinay 2169 found.append(Simulation.fromDom(self, node))
118 gross 918 elif node.tagName == 'Model':
119 caltinay 2169 found.append(self.getClassPath(node).fromDom(self, node))
120 gross 918 elif node.tagName == 'ParameterSet':
121 caltinay 2169 found.append(self.getClassPath(node).fromDom(self, node))
122 gross 918 else:
123 caltinay 2169 raise "Invalid type, %r" % node.getAttribute("type")
124 gross 918 self.setLinks()
125     return found
126    
127     def registerLink(self,obj_id, link):
128     self.__link_registry.append((int(obj_id),link))
129    
130     def registerLinkableObject(self,obj, node):
131     id_str=node.getAttribute('id').strip()
132     if len(id_str)>0:
133     id=int(id_str)
134     if self.__linkable_object_registry.has_key(id):
135     raise ValueError("Object id %s already exists."%id)
136     else:
137     self.__linkable_object_registry[id]=obj
138    
139     def getComponent(self, node):
140     """
141 caltinay 2169 Returns a single component + rank from a simulation.
142 gross 918 """
143     rank = int(node.getAttribute("rank"))
144     for n in node.childNodes:
145     if isinstance(n, minidom.Element):
146     if n.tagName == 'Simulation':
147     return (rank, Simulation.fromDom(self, n))
148     elif n.tagName == 'Model':
149     return (rank, self.getClassPath(n).fromDom(self, n))
150     elif n.tagName == 'ParameterSet':
151     return (rank, self.getClassPath(n).fromDom(self, n))
152     else:
153     raise ValueError("illegal component type %s"%n.tagName)
154     raise ValueError("cannot resolve Component")
155    
156     class ESySXMLCreator(object):
157     """
158 caltinay 2169 Creates an XML Dom representation.
159 gross 918 """
160     def __init__(self):
161 caltinay 2169 self.__dom=minidom.Document()
162     self.__esys =self.__dom.createElement('ESys')
163     self.__dom.appendChild(self.__esys)
164     self.__linkable_object_registry={}
165     self.__number_sequence = itertools.count(100)
166    
167 gross 918 def getRoot(self):
168 caltinay 2169 return self.__esys
169    
170 gross 918 def createElement(self,name):
171 caltinay 2169 return self.__dom.createElement(name)
172    
173 gross 918 def createTextNode(self,name):
174 caltinay 2169 return self.__dom.createTextNode(name)
175    
176 gross 918 def getElementById(self,name):
177 caltinay 2169 return self.__dom.getElementById(name)
178    
179 gross 918 def createDataNode(self, tagName, data):
180 caltinay 2169 """
181     C{createDataNode}s are the building blocks of the XML documents
182     constructed in this module.
183    
184     @param tagName: the associated XML tag
185     @param data: the values in the tag
186     """
187     n = self.createElement(tagName)
188     n.appendChild(self.createTextNode(str(data)))
189     return n
190    
191 gross 918 def getLinkableObjectId(self, obj):
192     for id, o in self.__linkable_object_registry.items():
193     if o == obj: return id
194     id =self.__number_sequence.next()
195     self.__linkable_object_registry[id]=obj
196     return id
197 caltinay 2169
198 gross 918 def registerLinkableObject(self, obj, node):
199     """
200 caltinay 2169 Returns a unique object id for object C{obj}.
201 gross 918 """
202     id=self.getLinkableObjectId(obj)
203     node.setAttribute('id',str(id))
204     node.setIdAttribute("id")
205    
206     def includeTargets(self):
207     target_written=True
208     while target_written:
209     targetsList =self.__dom.getElementsByTagName('Target')
210     target_written=False
211     for element in targetsList:
212     targetId = int(element.firstChild.nodeValue.strip())
213     if self.getElementById(str(targetId)): continue
214     targetObj = self.__linkable_object_registry[targetId]
215     targetObj.toDom(self, self.__esys)
216     target_written=True
217    
218     def toprettyxml(self):
219     self.includeTargets()
220     return self.__dom.toprettyxml()
221 caltinay 2169
222 jgs 122 class Link:
223 jgs 123 """
224 caltinay 2169 A Link makes an attribute of an object callable::
225 jgs 149
226 caltinay 2169 o.object()
227     o.a=8
228     l=Link(o,"a")
229     assert l()==8
230     """
231    
232 jgs 123 def __init__(self,target,attribute=None):
233 jgs 126 """
234 jgs 149 Creates a link to the object target. If attribute is given, the link is
235 caltinay 2169 established to this attribute of the target. Otherwise the attribute is
236 jgs 126 undefined.
237     """
238 jgs 123 self.target = target
239     self.attribute = None
240     self.setAttributeName(attribute)
241 gross 912
242 gross 938 def getTarget(self):
243     """
244 caltinay 2169 Returns the target.
245     """
246     return self.target
247    
248 gross 912 def getAttributeName(self):
249     """
250 caltinay 2169 Returns the name of the attribute the link is pointing to.
251 gross 912 """
252     return self.attribute
253 caltinay 2169
254 jgs 123 def setAttributeName(self,attribute):
255 jgs 126 """
256 caltinay 2169 Sets a new attribute name to be collected from the target object. The
257 jgs 126 target object must have the attribute with name attribute.
258     """
259 jgs 147 if attribute and self.target:
260     if isinstance(self.target,LinkableObject):
261     if not self.target.hasAttribute(attribute):
262     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
263     else:
264     if not hasattr(self.target,attribute):
265     raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute))
266 jgs 123 self.attribute = attribute
267 caltinay 2169
268 jgs 123 def hasDefinedAttributeName(self):
269 jgs 126 """
270 jgs 149 Returns true if an attribute name is set.
271 jgs 126 """
272 jgs 123 return self.attribute != None
273 caltinay 2169
274 jgs 123 def __repr__(self):
275 jgs 126 """
276 jgs 149 Returns a string representation of the link.
277 jgs 126 """
278 jgs 123 if self.hasDefinedAttributeName():
279     return "<Link to attribute %s of %s>" % (self.attribute, self.target)
280     else:
281     return "<Link to target %s>" % self.target
282 caltinay 2169
283 jgs 123 def __call__(self,name=None):
284 jgs 126 """
285 jgs 149 Returns the value of the attribute of the target object. If the
286 caltinay 2169 attribute is callable then the return value of the call is returned.
287 jgs 126 """
288 jgs 123 if name:
289     out=getattr(self.target, name)
290     else:
291     out=getattr(self.target, self.attribute)
292 jgs 121
293 jgs 123 if callable(out):
294     return out()
295     else:
296     return out
297 jgs 121
298 gross 918 def toDom(self, esysxml, node):
299 jgs 126 """
300 jgs 149 C{toDom} method of Link. Creates a Link node and appends it to the
301 caltinay 2169 current XML esysxml.
302 jgs 126 """
303 gross 918 link = esysxml.createElement('Link')
304 jgs 147 assert (self.target != None), ("Target was none, name was %r" % self.attribute)
305 gross 918 link.appendChild(esysxml.createDataNode('Target', esysxml.getLinkableObjectId(self.target)))
306 jgs 123 # this use of id will not work for purposes of being able to retrieve the intended
307     # target from the xml later. I need a better unique identifier.
308     assert self.attribute, "You can't xmlify a Link without a target attribute"
309 gross 918 link.appendChild(esysxml.createDataNode('Attribute', self.attribute))
310 jgs 123 node.appendChild(link)
311 jgs 121
312 gross 918 def fromDom(cls, esysxml, node):
313     targetid = int(node.getElementsByTagName("Target")[0].firstChild.nodeValue.strip())
314     attribute =str(node.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip())
315 jgs 123 l = cls(None, attribute)
316 gross 918 esysxml.registerLink(targetid, l)
317 jgs 123 return l
318 jgs 121
319 jgs 123 fromDom = classmethod(fromDom)
320 caltinay 2169
321 jgs 122 class LinkableObject(object):
322 jgs 123 """
323     An object that allows to link its attributes to attributes of other objects
324 jgs 149 via a Link object. For instance::
325 jgs 121
326 caltinay 2169 p = LinkableObject()
327     p.x = Link(o,"name")
328     print p.x
329 jgs 121
330 caltinay 2169 links attribute C{x} of C{p} to the attribute name of object C{o}.
331    
332     C{p.x} will contain the current value of attribute C{name} of object C{o}.
333    
334     If the value of C{getattr(o, "name")} is callable, C{p.x} will return
335     the return value of the call.
336 jgs 123 """
337 caltinay 2169
338 gross 918 def __init__(self, id = None, debug=False):
339 jgs 149 """
340 caltinay 2169 Initializes LinkableObject so that we can operate on Links.
341     """
342 jgs 123 self.debug = debug
343     self.__linked_attributes={}
344 caltinay 2169
345 jgs 123 def trace(self, msg):
346     """
347 caltinay 2169 If debugging is on, prints the message, otherwise does nothing.
348 jgs 149 """
349 jgs 123 if self.debug:
350 jgs 147 print "%s: %s"%(str(self),msg)
351 caltinay 2169
352 jgs 123 def __getattr__(self,name):
353 jgs 149 """
354 caltinay 2169 Returns the value of attribute name. If the value is a Link object the
355 jgs 149 object is called and the return value is returned.
356 caltinay 2169 """
357 jgs 123 out = self.getAttributeObject(name)
358     if isinstance(out,Link):
359     return out()
360     else:
361     return out
362 caltinay 2169
363 jgs 123 def getAttributeObject(self,name):
364 jgs 149 """
365 caltinay 2169 Returns the object stored for attribute C{name}.
366     """
367 jgs 123
368     if self.__dict__.has_key(name):
369     return self.__dict__[name]
370    
371     if self.__linked_attributes.has_key(name):
372     return self.__linked_attributes[name]
373    
374 jgs 147 if self.__class__.__dict__.has_key(name):
375     return self.__class.__dict__[name]
376    
377 jgs 123 raise AttributeError,"No attribute %s."%name
378 caltinay 2169
379 jgs 147 def hasAttribute(self,name):
380 jgs 149 """
381 caltinay 2169 Returns True if self has attribute C{name}.
382     """
383 jgs 147 return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name)
384    
385 jgs 123 def __setattr__(self,name,value):
386 jgs 149 """
387 caltinay 2169 Sets the value for attribute name. If value is a Link the target
388 jgs 149 attribute is set to name if no attribute has been specified.
389 caltinay 2169 """
390 jgs 123
391 caltinay 2169 if self.__dict__.has_key(name):
392 jgs 123 del self.__dict__[name]
393    
394     if isinstance(value,Link):
395 caltinay 2169 if not value.hasDefinedAttributeName():
396 jgs 123 value.setAttributeName(name)
397     self.__linked_attributes[name] = value
398    
399 jgs 147 self.trace("attribute %s is now linked by %s."%(name,value))
400 jgs 123 else:
401     self.__dict__[name] = value
402 caltinay 2169
403 jgs 123 def __delattr__(self,name):
404 jgs 149 """
405 caltinay 2169 Removes the attribute C{name}.
406     """
407 jgs 123
408     if self.__linked_attributes.has_key[name]:
409     del self.__linked_attributes[name]
410     elif self.__dict__.has_key(name):
411     del self.__dict__[name]
412     else:
413     raise AttributeError,"No attribute %s."%name
414    
415 jgs 122 class _ParameterIterator:
416 jgs 123 def __init__(self,parameterset):
417 jgs 121
418 jgs 123 self.__set=parameterset
419     self.__iter=iter(parameterset.parameters)
420    
421     def next(self):
422     o=self.__iter.next()
423     return (o,self.__set.getAttributeObject(o))
424    
425     def __iter__(self):
426     return self
427    
428 jgs 122 class ParameterSet(LinkableObject):
429 jgs 149 """
430 caltinay 2169 A class which allows to emphasize attributes to be written and read to XML.
431    
432 jgs 149 Leaves of an ESySParameters object can be:
433 caltinay 2169
434     - a real number
435     - an integer number
436     - a string
437     - a boolean value
438     - a ParameterSet object
439     - a Simulation object
440     - a Model object
441     - a numarray object
442     - a list of booleans
443 elspeth 874 - any other object (not considered by writeESySXML and writeXML)
444 caltinay 2169
445     Example for how to create an ESySParameters object::
446    
447 jgs 149 p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.)
448 caltinay 2169 p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11)
449 jgs 149 parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1")))
450 caltinay 2169
451 jgs 149 This can be accessed as::
452 caltinay 2169
453     parm.parm1.gamma=0.
454 jgs 149 parm.parm1.dim=2
455     parm.parm1.tol_v=0.001
456     parm.parm1.output_file="/tmp/u.%3.3d.dx"
457     parm.parm1.runFlag=True
458     parm.parm1.parm11.gamma1=1.
459     parm.parm1.parm11.gamma2=2.
460     parm.parm1.parm11.gamma3=3.
461     parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1)
462 jgs 123 """
463     def __init__(self, parameters=[], **kwargs):
464 jgs 149 """
465 caltinay 2169 Creates a ParameterSet with given parameters.
466     """
467 jgs 123 LinkableObject.__init__(self, **kwargs)
468     self.parameters = set()
469     self.declareParameters(parameters)
470 jgs 147
471     def __repr__(self):
472 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
473 caltinay 2169
474 jgs 123 def declareParameter(self,**parameters):
475 jgs 149 """
476 caltinay 2169 Declares one or more new parameters and their initial value.
477     """
478 jgs 123 self.declareParameters(parameters)
479 caltinay 2169
480 jgs 123 def declareParameters(self,parameters):
481 jgs 149 """
482 caltinay 2169 Declares a set of parameters. parameters can be a list, a dictionary
483     or a ParameterSet.
484     """
485 jgs 123 if isinstance(parameters,ListType):
486     parameters = zip(parameters, itertools.repeat(None))
487     if isinstance(parameters,DictType):
488     parameters = parameters.iteritems()
489 jgs 121
490 jgs 123 for prm, value in parameters:
491     setattr(self,prm,value)
492     self.parameters.add(prm)
493 jgs 121
494 jgs 123 def releaseParameters(self,name):
495 jgs 149 """
496 caltinay 2169 Removes parameter name from the parameters.
497     """
498     if self.isParameter(name):
499 jgs 123 self.parameters.remove(name)
500 jgs 147 self.trace("parameter %s has been removed."%name)
501 gross 950
502     def checkLinkTargets(self, models, hash):
503     """
504 caltinay 2169 Returns a set of tuples ("<self>(<name>)", <target model>) if the
505     parameter <name> is linked to model <target model> but <target model>
506     is not in the list of models. If a parameter is linked to another
507     parameter set which is not in the hash list the parameter set is
508     checked for its models. hash gives the call history.
509 gross 950 """
510 gross 952 out=set()
511 gross 950 for name, value in self:
512     if isinstance(value, Link):
513     m=value.getTarget()
514 gross 952 if isinstance(m, Model):
515     if not m in models: out.add( (str(self)+"("+name+")",m) )
516     elif isinstance(m, ParameterSet) and not m in hash:
517     out|=set( [ (str(self)+"("+name+")."+f[0],f[1]) for f in m.checkLinkTargets(models, hash+[ self ] ) ] )
518     return out
519 caltinay 2169
520 jgs 123 def __iter__(self):
521 jgs 149 """
522 caltinay 2169 Creates an iterator over the parameter and their values.
523     """
524 jgs 123 return _ParameterIterator(self)
525 caltinay 2169
526 jgs 123 def showParameters(self):
527 jgs 149 """
528 caltinay 2169 Returns a description of the parameters.
529     """
530 jgs 123 out="{"
531     notfirst=False
532     for i,v in self:
533     if notfirst: out=out+","
534     notfirst=True
535     if isinstance(v,ParameterSet):
536     out="%s\"%s\" : %s"%(out,i,v.showParameters())
537     else:
538     out="%s\"%s\" : %s"%(out,i,v)
539     return out+"}"
540 caltinay 2169
541 jgs 123 def __delattr__(self,name):
542 jgs 149 """
543 caltinay 2169 Removes the attribute C{name}.
544     """
545 jgs 123 LinkableObject.__delattr__(self,name)
546     try:
547 caltinay 2169 self.releaseParameter(name)
548 jgs 123 except:
549     pass
550 jgs 121
551 gross 918 def toDom(self, esysxml, node):
552 jgs 149 """
553 caltinay 2169 C{toDom} method of Model class.
554     """
555 gross 918 pset = esysxml.createElement('ParameterSet')
556     pset.setAttribute('type', self.__class__.__name__)
557     pset.setAttribute('module', self.__class__.__module__)
558     esysxml.registerLinkableObject(self, pset)
559     self._parametersToDom(esysxml, pset)
560 jgs 123 node.appendChild(pset)
561 jgs 121
562 gross 918 def _parametersToDom(self, esysxml, node):
563 jgs 123 for name,value in self:
564 gross 918 # convert list to numarray when possible:
565     if isinstance (value, list):
566     elem_type=-1
567     for i in value:
568 caltinay 2169 if isinstance(i, bool):
569 gross 918 elem_type = max(elem_type,0)
570 gross 964 elif isinstance(i, int):
571 gross 918 elem_type = max(elem_type,1)
572 gross 964 elif isinstance(i, float):
573 gross 918 elem_type = max(elem_type,2)
574     if elem_type == 0: value = numarray.array(value,numarray.Bool)
575     if elem_type == 1: value = numarray.array(value,numarray.Int)
576     if elem_type == 2: value = numarray.array(value,numarray.Float)
577    
578     param = esysxml.createElement('Parameter')
579 jgs 123 param.setAttribute('type', value.__class__.__name__)
580 jgs 121
581 gross 918 param.appendChild(esysxml.createDataNode('Name', name))
582 jgs 121
583 gross 918 val = esysxml.createElement('Value')
584 elspeth 875 if isinstance(value,(ParameterSet,Link,DataSource)):
585 gross 918 value.toDom(esysxml, val)
586 jgs 123 param.appendChild(val)
587 elspeth 871 elif isinstance(value, numarray.NumArray):
588     shape = value.getshape()
589     if isinstance(shape, tuple):
590     size = reduce(operator.mul, shape)
591     shape = ' '.join(map(str, shape))
592     else:
593     size = shape
594     shape = str(shape)
595    
596     arraytype = value.type()
597 gross 918 if isinstance(arraytype, numarray.BooleanType):
598     arraytype_str="Bool"
599     elif isinstance(arraytype, numarray.IntegralType):
600     arraytype_str="Int"
601     elif isinstance(arraytype, numarray.FloatingType):
602     arraytype_str="Float"
603     elif isinstance(arraytype, numarray.ComplexType):
604     arraytype_str="Complex"
605     else:
606     arraytype_str=str(arraytype)
607     numarrayElement = esysxml.createElement('NumArray')
608     numarrayElement.appendChild(esysxml.createDataNode('ArrayType', arraytype_str))
609     numarrayElement.appendChild(esysxml.createDataNode('Shape', shape))
610     numarrayElement.appendChild(esysxml.createDataNode('Data', ' '.join(
611 elspeth 871 [str(x) for x in numarray.reshape(value, size)])))
612 elspeth 874 val.appendChild(numarrayElement)
613 elspeth 871 param.appendChild(val)
614 gross 918 elif isinstance(value, list):
615     param.appendChild(esysxml.createDataNode('Value', ' '.join([str(x) for x in value]) ))
616     elif isinstance(value, (str, bool, int, float, type(None))):
617     param.appendChild(esysxml.createDataNode('Value', str(value)))
618 gross 926 elif isinstance(value, dict):
619     dic = esysxml.createElement('dictionary')
620     if len(value.keys())>0:
621     dic.setAttribute('key_type', value.keys()[0].__class__.__name__)
622     dic.setAttribute('value_type', value[value.keys()[0]].__class__.__name__)
623     for k,v in value.items():
624     i=esysxml.createElement('item')
625     i.appendChild(esysxml.createDataNode('key', k))
626     i.appendChild(esysxml.createDataNode('value', v))
627     dic.appendChild(i)
628     param.appendChild(dic)
629 jgs 123 else:
630 gross 918 raise ValueError("cannot serialize %s type to XML."%str(value.__class__))
631 jgs 123
632     node.appendChild(param)
633    
634 gross 918 def fromDom(cls, esysxml, node):
635 jgs 123 # Define a host of helper functions to assist us.
636     def _children(node):
637     """
638 jgs 149 Remove the empty nodes from the children of this node.
639 jgs 123 """
640 elspeth 871 ret = []
641     for x in node.childNodes:
642     if isinstance(x, minidom.Text):
643     if x.nodeValue.strip():
644     ret.append(x)
645     else:
646     ret.append(x)
647     return ret
648 jgs 123
649 gross 918 def _floatfromValue(esysxml, node):
650     return float(node.nodeValue.strip())
651 jgs 123
652 gross 918 def _stringfromValue(esysxml, node):
653     return str(node.nodeValue.strip())
654 caltinay 2169
655 gross 918 def _intfromValue(esysxml, node):
656     return int(node.nodeValue.strip())
657 jgs 126
658 gross 918 def _boolfromValue(esysxml, node):
659     return _boolfromstring(node.nodeValue.strip())
660 elspeth 266
661 gross 918 def _nonefromValue(esysxml, node):
662 elspeth 266 return None
663 elspeth 871
664 gross 918 def _numarrayfromValue(esysxml, node):
665     for node in _children(node):
666 elspeth 871 if node.tagName == 'ArrayType':
667     arraytype = node.firstChild.nodeValue.strip()
668     if node.tagName == 'Shape':
669     shape = node.firstChild.nodeValue.strip()
670     shape = [int(x) for x in shape.split()]
671     if node.tagName == 'Data':
672     data = node.firstChild.nodeValue.strip()
673     data = [float(x) for x in data.split()]
674     return numarray.reshape(numarray.array(data, type=getattr(numarray, arraytype)),
675     shape)
676 caltinay 2169
677 gross 918 def _listfromValue(esysxml, node):
678     return [x for x in node.nodeValue.split()]
679 elspeth 874
680     def _boolfromstring(s):
681     if s == 'True':
682     return True
683     else:
684     return False
685 jgs 123 # Mapping from text types in the xml to methods used to process trees of that type
686     ptypemap = {"Simulation": Simulation.fromDom,
687     "Model":Model.fromDom,
688     "ParameterSet":ParameterSet.fromDom,
689     "Link":Link.fromDom,
690 elspeth 875 "DataSource":DataSource.fromDom,
691 jgs 123 "float":_floatfromValue,
692 jgs 126 "int":_intfromValue,
693 jgs 123 "str":_stringfromValue,
694 elspeth 269 "bool":_boolfromValue,
695 elspeth 874 "list":_listfromValue,
696     "NumArray":_numarrayfromValue,
697 elspeth 871 "NoneType":_nonefromValue,
698 jgs 123 }
699    
700     parameters = {}
701 gross 918 for n in _children(node):
702     ptype = n.getAttribute("type")
703     if not ptypemap.has_key(ptype):
704     raise KeyError("cannot handle parameter type %s."%ptype)
705 jgs 123
706     pname = pvalue = None
707 gross 918 for childnode in _children(n):
708 jgs 123 if childnode.tagName == "Name":
709     pname = childnode.firstChild.nodeValue.strip()
710    
711     if childnode.tagName == "Value":
712     nodes = _children(childnode)
713 gross 918 pvalue = ptypemap[ptype](esysxml, nodes[0])
714 jgs 123
715     parameters[pname] = pvalue
716    
717     # Create the instance of ParameterSet
718 gross 1042 try:
719     o = cls(debug=esysxml.debug)
720     except TypeError, inst:
721     print inst.args[0]
722     if inst.args[0]=="__init__() got an unexpected keyword argument 'debug'":
723     raise TypeError("The Model class %s __init__ needs to have argument 'debug'.")
724     else:
725     raise inst
726 jgs 123 o.declareParameters(parameters)
727 gross 918 esysxml.registerLinkableObject(o, node)
728 jgs 123 return o
729 caltinay 2169
730 jgs 123 fromDom = classmethod(fromDom)
731 gross 918
732 jgs 123 def writeXML(self,ostream=stdout):
733 jgs 149 """
734 caltinay 2169 Writes the object as an XML object into an output stream.
735     """
736 gross 918 esysxml=ESySXMLCreator()
737     self.toDom(esysxml, esysxml.getRoot())
738     ostream.write(esysxml.toprettyxml())
739 caltinay 2169
740 jgs 147 class Model(ParameterSet):
741     """
742 caltinay 2169 A Model object represents a process marching over time until a
743     finalizing condition is fulfilled. At each time step an iterative
744 jgs 149 process can be performed and the time step size can be controlled. A
745     Model has the following work flow::
746 jgs 121
747 caltinay 2169 doInitialization()
748     while not terminateInitialIteration(): doInitializationiStep()
749     doInitialPostprocessing()
750     while not finalize():
751     dt=getSafeTimeStepSize(dt)
752     doStepPreprocessing(dt)
753     while not terminateIteration(): doStep(dt)
754     doStepPostprocessing(dt)
755     doFinalization()
756    
757 jgs 149 where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
758     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
759     C{doFinalization} are methods of the particular instance of a Model. The
760     default implementations of these methods have to be overwritten by the
761     subclass implementing a Model.
762 jgs 147 """
763 jgs 121
764 jgs 147 UNDEF_DT=1.e300
765 jgs 121
766 gross 918 def __init__(self,parameters=[],**kwargs):
767 jgs 149 """
768 caltinay 2169 Creates a model.
769 jgs 121
770 jgs 149 Just calls the parent constructor.
771 jgs 147 """
772 gross 918 ParameterSet.__init__(self, parameters=parameters,**kwargs)
773 jgs 121
774 jgs 147 def __str__(self):
775 gross 908 return "<%s %d>"%(self.__class__.__name__,id(self))
776 jgs 121
777    
778 gross 975 def setUp(self):
779     """
780 caltinay 2169 Sets up the model.
781 gross 975
782     This function may be overwritten.
783     """
784     pass
785    
786 jgs 147 def doInitialization(self):
787 jgs 149 """
788 caltinay 2169 Initializes the time stepping scheme. This method is not called in
789     case of a restart.
790    
791     This function may be overwritten.
792     """
793 jgs 147 pass
794 caltinay 2169
795 gross 906 def doInitialStep(self):
796     """
797 caltinay 2169 Performs an iteration step in the initialization phase. This method
798     is not called in case of a restart.
799 gross 906
800 caltinay 2169 This function may be overwritten.
801     """
802 gross 906 pass
803    
804     def terminateInitialIteration(self):
805     """
806 caltinay 2169 Returns True if iteration at the inital phase is terminated.
807     """
808 gross 906 return True
809    
810     def doInitialPostprocessing(self):
811     """
812 caltinay 2169 Finalises the initialization iteration process. This method is not
813     called in case of a restart.
814 gross 906
815 caltinay 2169 This function may be overwritten.
816     """
817 gross 906 pass
818 caltinay 2169
819 jgs 147 def getSafeTimeStepSize(self,dt):
820 jgs 149 """
821 caltinay 2169 Returns a time step size which can be safely used.
822 jgs 149
823     C{dt} gives the previously used step size.
824    
825     This function may be overwritten.
826 caltinay 2169 """
827 jgs 147 return self.UNDEF_DT
828 caltinay 2169
829 jgs 147 def finalize(self):
830 jgs 149 """
831 caltinay 2169 Returns False if the time stepping is finalized.
832    
833     This function may be overwritten.
834     """
835 jgs 147 return False
836 caltinay 2169
837 jgs 147 def doFinalization(self):
838 jgs 149 """
839 caltinay 2169 Finalizes the time stepping.
840    
841     This function may be overwritten.
842     """
843 jgs 147 pass
844 caltinay 2169
845 jgs 147 def doStepPreprocessing(self,dt):
846 jgs 149 """
847 caltinay 2169 Sets up a time step of step size dt.
848    
849     This function may be overwritten.
850     """
851 jgs 147 pass
852 caltinay 2169
853 jgs 147 def doStep(self,dt):
854 jgs 149 """
855 caltinay 2169 Executes an iteration step at a time step.
856 jgs 149
857     C{dt} is the currently used time step size.
858    
859     This function may be overwritten.
860 caltinay 2169 """
861 jgs 147 pass
862 caltinay 2169
863 jgs 147 def terminateIteration(self):
864 jgs 149 """
865 caltinay 2169 Returns True if iteration on a time step is terminated.
866     """
867 jgs 147 return True
868 gross 906
869 jgs 147 def doStepPostprocessing(self,dt):
870 jgs 149 """
871 caltinay 2169 Finalises the time step.
872 jgs 149
873     dt is the currently used time step size.
874    
875     This function may be overwritten.
876 caltinay 2169 """
877 jgs 147 pass
878 gross 918
879     def toDom(self, esysxml, node):
880     """
881 caltinay 2169 C{toDom} method of Model class.
882     """
883 gross 918 pset = esysxml.createElement('Model')
884     pset.setAttribute('type', self.__class__.__name__)
885     pset.setAttribute('module', self.__class__.__module__)
886     esysxml.registerLinkableObject(self, pset)
887     node.appendChild(pset)
888     self._parametersToDom(esysxml, pset)
889 caltinay 2169
890     class Simulation(Model):
891 jgs 147 """
892 caltinay 2169 A Simulation object is a special Model which runs a sequence of Models.
893 jgs 121
894 jgs 149 The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize},
895     C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing},
896 caltinay 2169 C{doFinalization} execute the corresponding methods of the models in
897 jgs 149 the simulation.
898 jgs 147 """
899 caltinay 2169
900 jgs 147 FAILED_TIME_STEPS_MAX=20
901     MAX_ITER_STEPS=50
902 gross 836 MAX_CHANGE_OF_DT=2.
903 caltinay 2169
904 jgs 147 def __init__(self, models=[], **kwargs):
905 jgs 149 """
906 caltinay 2169 Initiates a simulation from a list of models.
907     """
908 gross 918 super(Simulation, self).__init__(**kwargs)
909 gross 975 self.declareParameter(time=0.,
910     time_step=0,
911     dt = self.UNDEF_DT)
912 gross 938 for m in models:
913     if not isinstance(m, Model):
914     raise TypeError("%s is not a subclass of Model."%m)
915 jgs 147 self.__models=[]
916 caltinay 2169 for i in range(len(models)):
917 jgs 147 self[i] = models[i]
918 jgs 121
919 caltinay 2169
920 jgs 147 def __repr__(self):
921     """
922 jgs 149 Returns a string representation of the Simulation.
923 jgs 147 """
924     return "<Simulation %r>" % self.__models
925 jgs 121
926 jgs 147 def __str__(self):
927     """
928 caltinay 2169 Returns Simulation as a string.
929 jgs 147 """
930 caltinay 2169 return "<Simulation %d>" % id(self)
931    
932 jgs 147 def iterModels(self):
933 jgs 149 """
934 caltinay 2169 Returns an iterator over the models.
935     """
936 jgs 147 return self.__models
937 caltinay 2169
938 jgs 147 def __getitem__(self,i):
939 jgs 149 """
940 caltinay 2169 Returns the i-th model.
941     """
942 jgs 147 return self.__models[i]
943 caltinay 2169
944 jgs 147 def __setitem__(self,i,value):
945 jgs 149 """
946 caltinay 2169 Sets the i-th model.
947     """
948 jgs 147 if not isinstance(value,Model):
949 gross 728 raise ValueError,"assigned value is not a Model but instance of %s"%(value.__class__.__name__,)
950 caltinay 2169 for j in range(max(i-len(self.__models)+1,0)):
951 jgs 149 self.__models.append(None)
952 jgs 147 self.__models[i]=value
953 caltinay 2169
954 jgs 147 def __len__(self):
955 jgs 149 """
956 caltinay 2169 Returns the number of models.
957     """
958 jgs 147 return len(self.__models)
959 jgs 121
960 gross 938 def getAllModels(self):
961     """
962 caltinay 2169 Returns a list of all models used in the Simulation including
963     subsimulations.
964 gross 938 """
965     out=[]
966     for m in self.iterModels():
967     if isinstance(m, Simulation):
968     out+=m.getAllModels()
969     else:
970     out.append(m)
971     return list(set(out))
972    
973 gross 950 def checkModels(self, models, hash):
974 gross 938 """
975 caltinay 2169 Returns a list of (model, parameter, target model) if the parameter
976     of model is linking to the target_model which is not in the list of
977     models.
978 gross 938 """
979 gross 952 out=self.checkLinkTargets(models, hash + [self])
980 gross 938 for m in self.iterModels():
981     if isinstance(m, Simulation):
982 gross 952 out|=m.checkModels(models, hash)
983 gross 938 else:
984 gross 952 out|=m.checkLinkTargets(models, hash + [self])
985     return set( [ (str(self)+"."+f[0],f[1]) for f in out ] )
986 gross 938
987 caltinay 2169
988 jgs 147 def getSafeTimeStepSize(self,dt):
989 jgs 149 """
990 caltinay 2169 Returns a time step size which can be safely used by all models.
991 jgs 149
992     This is the minimum over the time step sizes of all models.
993 caltinay 2169 """
994 jgs 147 out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()])
995     return out
996 gross 975
997     def setUp(self):
998     """
999 caltinay 2169 Performs the setup for all models.
1000 gross 975 """
1001 caltinay 2169 for o in self.iterModels():
1002 gross 975 o.setUp()
1003 caltinay 2169
1004 jgs 147 def doInitialization(self):
1005 jgs 149 """
1006 caltinay 2169 Initializes all models.
1007     """
1008     for o in self.iterModels():
1009 gross 906 o.doInitialization()
1010 caltinay 2169
1011 gross 906 def doInitialStep(self):
1012     """
1013 caltinay 2169 Performs an iteration step in the initialization step for all models.
1014     """
1015 gross 908 iter=0
1016     while not self.terminateInitialIteration():
1017     if iter==0: self.trace("iteration for initialization starts")
1018     iter+=1
1019     self.trace("iteration step %d"%(iter))
1020 caltinay 2169 for o in self.iterModels():
1021 gross 908 o.doInitialStep()
1022     if iter>self.MAX_ITER_STEPS:
1023     raise IterationDivergenceError("initial iteration did not converge after %s steps."%iter)
1024     self.trace("Initialization finalized after %s iteration steps."%iter)
1025 gross 906
1026     def doInitialPostprocessing(self):
1027     """
1028 caltinay 2169 Finalises the initialization iteration process for all models.
1029     """
1030     for o in self.iterModels():
1031 gross 906 o.doInitialPostprocessing()
1032 caltinay 2169
1033 jgs 147 def finalize(self):
1034 jgs 149 """
1035 caltinay 2169 Returns True if any of the models is to be finalized.
1036     """
1037 jgs 147 return any([o.finalize() for o in self.iterModels()])
1038 caltinay 2169
1039 jgs 147 def doFinalization(self):
1040 jgs 149 """
1041 caltinay 2169 Finalises the time stepping for all models.
1042     """
1043 jgs 147 for i in self.iterModels(): i.doFinalization()
1044     self.trace("end of time integation.")
1045 caltinay 2169
1046 jgs 147 def doStepPreprocessing(self,dt):
1047 jgs 149 """
1048 caltinay 2169 Initializes the time step for all models.
1049     """
1050     for o in self.iterModels():
1051 jgs 147 o.doStepPreprocessing(dt)
1052 caltinay 2169
1053 jgs 147 def terminateIteration(self):
1054 jgs 149 """
1055 caltinay 2169 Returns True if all iterations for all models are terminated.
1056     """
1057 jgs 147 out=all([o.terminateIteration() for o in self.iterModels()])
1058     return out
1059 gross 906
1060     def terminateInitialIteration(self):
1061     """
1062 caltinay 2169 Returns True if all initial iterations for all models are terminated.
1063     """
1064 gross 906 out=all([o.terminateInitialIteration() for o in self.iterModels()])
1065     return out
1066 caltinay 2169
1067 jgs 147 def doStepPostprocessing(self,dt):
1068 jgs 149 """
1069 caltinay 2169 Finalises the iteration process for all models.
1070     """
1071     for o in self.iterModels():
1072 jgs 147 o.doStepPostprocessing(dt)
1073 gross 975 self.time_step+=1
1074     self.time+=dt
1075     self.dt=dt
1076 caltinay 2169
1077 jgs 147 def doStep(self,dt):
1078     """
1079 caltinay 2169 Executes the iteration step at a time step for all models::
1080    
1081 jgs 149 self.doStepPreprocessing(dt)
1082 caltinay 2169 while not self.terminateIteration():
1083     for all models:
1084     self.doStep(dt)
1085 jgs 149 self.doStepPostprocessing(dt)
1086 jgs 147 """
1087     self.iter=0
1088 caltinay 2169 while not self.terminateIteration():
1089 gross 975 if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.time_step+1,self.time+dt))
1090 jgs 147 self.iter+=1
1091     self.trace("iteration step %d"%(self.iter))
1092 caltinay 2169 for o in self.iterModels():
1093 jgs 147 o.doStep(dt)
1094 gross 975 if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.time_step+1,self.time+dt))
1095 jgs 121
1096 gross 975 def run(self,check_pointing=None):
1097 jgs 147 """
1098 caltinay 2169 Runs the simulation by performing essentially::
1099    
1100 gross 975 self.setUp()
1101     if not restart:
1102 caltinay 2169 self.doInitialization()
1103 gross 975 while not self.terminateInitialIteration(): self.doInitialStep()
1104     self.doInitialPostprocessing()
1105 caltinay 2169 while not self.finalize():
1106     dt=self.getSafeTimeStepSize()
1107 gross 975 self.doStepPreprocessing(dt_new)
1108     self.doStep(dt_new)
1109     self.doStepPostprocessing(dt_new)
1110 caltinay 2169 self.doFinalization()
1111 jgs 121
1112 caltinay 2169 If one of the models throws a C{FailedTimeStepError} exception a
1113     new time step size is computed through getSafeTimeStepSize() and the
1114     time step is repeated.
1115 jgs 121
1116 caltinay 2169 If one of the models throws a C{IterationDivergenceError}
1117     exception the time step size is halved and the time step is repeated.
1118    
1119 jgs 149 In both cases the time integration is given up after
1120 caltinay 2169 C{Simulation.FAILED_TIME_STEPS_MAX} attempts.
1121 jgs 147 """
1122 caltinay 2169 # check the completeness of the models:
1123     # first a list of all the models involved in the simulation including
1124     # subsimulations:
1125     #
1126 gross 950 missing=self.checkModels(self.getAllModels(), [])
1127 gross 938 if len(missing)>0:
1128     msg=""
1129     for l in missing:
1130 gross 952 msg+="\n\t"+str(l[1])+" at "+l[0]
1131 gross 938 raise MissingLink("link targets missing in the Simulation: %s"%msg)
1132     #==============================
1133 gross 975 self.setUp()
1134     if self.time_step < 1:
1135     self.doInitialization()
1136     self.doInitialStep()
1137     self.doInitialPostprocessing()
1138 jgs 147 while not self.finalize():
1139     step_fail_counter=0
1140     iteration_fail_counter=0
1141 gross 975 if self.time_step==0:
1142 gross 980 dt_new=self.getSafeTimeStepSize(self.dt)
1143 gross 836 else:
1144 gross 975 dt_new=min(max(self.getSafeTimeStepSize(self.dt),self.dt/self.MAX_CHANGE_OF_DT),self.dt*self.MAX_CHANGE_OF_DT)
1145     self.trace("%d. time step %e (step size %e.)" % (self.time_step+1,self.time+dt_new,dt_new))
1146 jgs 147 end_of_step=False
1147     while not end_of_step:
1148     end_of_step=True
1149     if not dt_new>0:
1150 gross 975 raise NonPositiveStepSizeError("non-positive step size in step %d"%(self.time_step+1))
1151 jgs 147 try:
1152     self.doStepPreprocessing(dt_new)
1153     self.doStep(dt_new)
1154     self.doStepPostprocessing(dt_new)
1155     except IterationDivergenceError:
1156     dt_new*=0.5
1157     end_of_step=False
1158     iteration_fail_counter+=1
1159     if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX:
1160 gross 836 raise SimulationBreakDownError("reduction of time step to achieve convergence failed after %s steps."%self.FAILED_TIME_STEPS_MAX)
1161     self.trace("Iteration failed. Time step is repeated with new step size %s."%dt_new)
1162 jgs 147 except FailedTimeStepError:
1163 gross 975 dt_new=self.getSafeTimeStepSize(self.dt)
1164 jgs 147 end_of_step=False
1165     step_fail_counter+=1
1166 gross 836 self.trace("Time step is repeated with new time step size %s."%dt_new)
1167 jgs 147 if step_fail_counter>self.FAILED_TIME_STEPS_MAX:
1168 gross 836 raise SimulationBreakDownError("Time integration is given up after %d attempts."%step_fail_counter)
1169 gross 980 if not check_pointing==None:
1170     if check_pointing.doDump():
1171 jgs 147 self.trace("check point is created.")
1172     self.writeXML()
1173     self.doFinalization()
1174 jgs 121
1175 gross 918
1176     def toDom(self, esysxml, node):
1177     """
1178 caltinay 2169 C{toDom} method of Simulation class.
1179     """
1180 gross 918 simulation = esysxml.createElement('Simulation')
1181     esysxml.registerLinkableObject(self, simulation)
1182     for rank, sim in enumerate(self.iterModels()):
1183     component = esysxml.createElement('Component')
1184     component.setAttribute('rank', str(rank))
1185     sim.toDom(esysxml, component)
1186     simulation.appendChild(component)
1187     node.appendChild(simulation)
1188    
1189     def fromDom(cls, esysxml, node):
1190 jgs 147 sims = []
1191 gross 918 for n in node.childNodes:
1192     if isinstance(n, minidom.Text):
1193 jgs 147 continue
1194 gross 918 sims.append(esysxml.getComponent(n))
1195 phornby 1020 sims.sort(_comp)
1196 gross 918 sim=cls([s[1] for s in sims], debug=esysxml.debug)
1197     esysxml.registerLinkableObject(sim, node)
1198     return sim
1199 jgs 121
1200 jgs 147 fromDom = classmethod(fromDom)
1201 jgs 121
1202 gross 918 def _comp(a,b):
1203     if a[0]<a[1]:
1204 caltinay 2169 return 1
1205 gross 918 elif a[0]>a[1]:
1206 caltinay 2169 return -1
1207 gross 918 else:
1208 caltinay 2169 return 0
1209 jgs 121
1210 jgs 147 class IterationDivergenceError(Exception):
1211     """
1212 caltinay 2169 Exception which is thrown if there is no convergence of the iteration
1213 jgs 149 process at a time step.
1214 jgs 121
1215 jgs 149 But there is a chance that a smaller step could help to reach convergence.
1216 jgs 147 """
1217     pass
1218 jgs 121
1219 jgs 147 class FailedTimeStepError(Exception):
1220 jgs 149 """
1221 caltinay 2169 Exception which is thrown if the time step fails because of a step
1222     size that has been chosen to be too large.
1223 jgs 149 """
1224 jgs 147 pass
1225 jgs 121
1226 jgs 147 class SimulationBreakDownError(Exception):
1227 jgs 149 """
1228 caltinay 2169 Exception which is thrown if the simulation does not manage to progress
1229     in time.
1230 jgs 149 """
1231 jgs 147 pass
1232 jgs 121
1233 jgs 147 class NonPositiveStepSizeError(Exception):
1234 jgs 149 """
1235     Exception which is thrown if the step size is not positive.
1236     """
1237 jgs 147 pass
1238 jgs 149
1239 gross 938 class MissingLink(Exception):
1240     """
1241 caltinay 2169 Exception which is thrown when a link is missing.
1242 gross 938 """
1243     pass
1244    
1245 elspeth 875 class DataSource(object):
1246     """
1247 caltinay 2169 Class for handling data sources, including local and remote files.
1248     This class is under development.
1249 elspeth 875 """
1250    
1251 gross 885 def __init__(self, uri="file.ext", fileformat="unknown"):
1252 elspeth 875 self.uri = uri
1253     self.fileformat = fileformat
1254    
1255 gross 918 def toDom(self, esysxml, node):
1256 elspeth 875 """
1257 caltinay 2169 C{toDom} method of DataSource. Creates a DataSource node and appends
1258     it to the current XML esysxml.
1259 elspeth 875 """
1260 gross 918 ds = esysxml.createElement('DataSource')
1261     ds.appendChild(esysxml.createDataNode('URI', self.uri))
1262     ds.appendChild(esysxml.createDataNode('FileFormat', self.fileformat))
1263 elspeth 875 node.appendChild(ds)
1264    
1265 gross 918 def fromDom(cls, esysxml, node):
1266 gross 920 uri= str(node.getElementsByTagName("URI")[0].firstChild.nodeValue.strip())
1267     fileformat= str(node.getElementsByTagName("FileFormat")[0].firstChild.nodeValue.strip())
1268 elspeth 875 ds = cls(uri, fileformat)
1269     return ds
1270    
1271 gross 885 def getLocalFileName(self):
1272     return self.uri
1273    
1274 elspeth 875 fromDom = classmethod(fromDom)
1275 gross 975
1276     class RestartManager(object):
1277     """
1278 caltinay 2169 A restart manager which does two things: it decides when restart files
1279     were created (when doDump returns true) and manages directories for
1280     restart files. The method getNewDumper creates a new directory and
1281     returns its name.
1282 gross 975
1283 caltinay 2169 This restart manager will decide to dump restart files every dump_step
1284     calls of doDump or if more than dump_time since the last dump has
1285     elapsed. The restart manager controls two directories for dumping
1286     restart data, namely for the current and previous dump. This way the
1287     previous dump can be used for restart in the case the current dump failed.
1288    
1289     @cvar SEC: unit of seconds, for instance 5*RestartManager.SEC defines 5 seconds
1290     @cvar MIN: unit of minutes, for instance 5*RestartManager.MIN defines 5 minutes
1291     @cvar H: unit of hours, for instance 5*RestartManager.H defines 5 hours
1292     @cvar D: unit of days, for instance 5*RestartManager.D defines 5 days
1293 gross 975 """
1294     SEC=1.
1295     MIN=60.
1296     H=360.
1297     D=8640.
1298     def __init__(self,dump_time=1080., dump_step=None, dumper=None):
1299     """
1300 caltinay 2169 Initializes the RestartManager.
1301 gross 975
1302 caltinay 2169 @param dump_time: defines the minimum time interval in SEC between two
1303     dumps. If C{None}, time is not used as criterion.
1304     @param dump_step: defines the number of calls of doDump between two
1305     dump events. If C{None}, the call counter is not
1306     used as criterion.
1307     @param dumper: defines the directory for dumping restart files.
1308     Additionally, the directories dumper+"_bkp" and
1309     dumper+"_bkp2" are used. If the directory does not
1310     exist it is created. If dumper is not present a unique
1311     directory within the current working directory is used.
1312 gross 975 """
1313     self.__dump_step=dump_time
1314     self.__dump_time=dump_step
1315     self.__counter=0
1316     self.__saveMarker()
1317     if dumper == None:
1318     self.__dumper="restart"+str(os.getpid())
1319     else:
1320     self.__dumper=dumper
1321     self.__dumper_bkp=self.__dumper+"_bkp"
1322     self.__dumper_bkp2=self.__dumper+"_bkp2"
1323     self.__current_dumper=None
1324 caltinay 2169
1325 gross 975 def __saveMarker(self):
1326     self.__last_restart_time=time.time()
1327     self.__last_restart_counter=self.__counter
1328 caltinay 2169
1329 gross 975 def getCurrentDumper(self):
1330     """
1331 caltinay 2169 Returns the name of the currently used dumper.
1332 gross 975 """
1333     return self.__current_dumper
1334 caltinay 2169
1335 gross 975 def doDump(self):
1336     """
1337 caltinay 2169 Returns true if the restart should be dumped. Use C{getNewDumper} to
1338     retrieve the directory name to be used for dumping.
1339     """
1340 gross 975 if self.__dump_step == None:
1341     if self.__dump_step == None:
1342     out = False
1343     else:
1344     out = (self.__dump_step + self.__last_restart_counter) <= self.__counter
1345     else:
1346     if dump_step == None:
1347     out = (self.__last_restart_time + self.__dump_time) <= time.time()
1348     else:
1349 caltinay 2169 out = ( (self.__dump_step + self.__last_restart_counter) <= self.__counter) \
1350 gross 975 or ( (self.__last_restart_time + self.__dump_time) <= time.time() )
1351     if out: self.__saveMarker()
1352     self__counter+=1
1353 caltinay 2169
1354 gross 975 def getNewDumper(self):
1355     """
1356 caltinay 2169 Creates a new directory to be used for dumping and returns its name.
1357     """
1358 gross 975 if os.access(self.__dumper_bkp,os.F_OK):
1359     if os.access(self.__dumper_bkp2, os.F_OK):
1360     raise RunTimeError("please remove %s."%self.__dumper_bkp2)
1361     try:
1362     os.rename(self.__dumper_bkp, self.__dumper_bkp2)
1363     except:
1364     self.__current_dumper=self.__dumper
1365 caltinay 2169 raise RunTimeError("renaming backup directory %s failed. Use %s for restart."%(self.__dumper_bkp,self.__dumper))
1366 gross 975 if os.access(self.__dumper,os.F_OK):
1367     if os.access(self.__dumper_bkp, os.F_OK):
1368     raise RunTimeError("please remove %s."%self.__dumper_bkp)
1369     try:
1370     os.rename(self.__dumper, self.__dumper_bkp)
1371     except:
1372     self.__current_dumper=self.__dumper_bkp2
1373 caltinay 2169 raise RunTimeError("moving directory %s to backup failed. Use %s for restart."%(self.__dumper,self.__dumper_bkp2))
1374     try:
1375 gross 975 os.mkdir(self.__dumper)
1376     except:
1377     self.__current_dumper=self.__dumper_bkp
1378     raise RunTimeError("creating a new restart directory %s failed. Use %s for restart."%(self.__dumper,self.__dumper_bkp))
1379     if os.access(self.__dumper_bkp2, os.F_OK): os.rmdir(self.__dumper_bkp2)
1380     return self.getCurrentDumper()
1381 caltinay 2169
1382    
1383 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