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: |