1 |
# $Id$ |
2 |
|
3 |
from types import StringType,IntType,FloatType,BooleanType,ListType,DictType |
4 |
from sys import stdout |
5 |
import itertools |
6 |
# import modellib temporarily removed!!! |
7 |
|
8 |
# import the 'set' module if it's not defined (python2.3/2.4 difference) |
9 |
try: |
10 |
set |
11 |
except NameError: |
12 |
from sets import Set as set |
13 |
|
14 |
from xml.dom import minidom |
15 |
|
16 |
def dataNode(document, tagName, data): |
17 |
""" |
18 |
dataNodes are the building blocks of the xml documents constructed in |
19 |
this module. document is the current xml document, tagName is the |
20 |
associated xml tag, and data is the values in the tag. |
21 |
""" |
22 |
t = document.createTextNode(str(data)) |
23 |
n = document.createElement(tagName) |
24 |
n.appendChild(t) |
25 |
return n |
26 |
|
27 |
def esysDoc(): |
28 |
""" |
29 |
Global method for creating an instance of an EsysXML document. |
30 |
""" |
31 |
doc = minidom.Document() |
32 |
esys = doc.createElement('ESys') |
33 |
doc.appendChild(esys) |
34 |
return doc, esys |
35 |
|
36 |
def all(seq): |
37 |
for x in seq: |
38 |
if not x: |
39 |
return False |
40 |
return True |
41 |
|
42 |
def any(seq): |
43 |
for x in seq: |
44 |
if x: |
45 |
return True |
46 |
return False |
47 |
|
48 |
LinkableObjectRegistry = {} |
49 |
|
50 |
def registerLinkableObject(obj_id, o): |
51 |
LinkableObjectRegistry[obj_id] = o |
52 |
|
53 |
LinkRegistry = [] |
54 |
|
55 |
def registerLink(obj_id, l): |
56 |
LinkRegistry.append((obj_id,l)) |
57 |
|
58 |
def parse(xml): |
59 |
""" |
60 |
Generic parse method for EsysXML. Without this, Links don't work. |
61 |
""" |
62 |
global LinkRegistry, LinkableObjectRegistry |
63 |
LinkRegistry = [] |
64 |
LinkableObjectRegistry = {} |
65 |
|
66 |
doc = minidom.parseString(xml) |
67 |
sim = getComponent(doc.firstChild) |
68 |
for obj_id, link in LinkRegistry: |
69 |
link.target = LinkableObjectRegistry[obj_id] |
70 |
|
71 |
return sim |
72 |
|
73 |
def getComponent(doc): |
74 |
""" |
75 |
Used to get components of Simualtions, Models. |
76 |
""" |
77 |
for node in doc.childNodes: |
78 |
|
79 |
if isinstance(node, minidom.Element): |
80 |
if node.tagName == 'Simulation': |
81 |
if node.getAttribute("type") == 'Simulation': |
82 |
return Simulation.fromDom(node) |
83 |
if node.tagName == 'Model': |
84 |
model_type = node.getAttribute("type") |
85 |
model_subclasses = Model.__subclasses__() |
86 |
for model in model_subclasses: |
87 |
if model_type == model.__name__: |
88 |
return Model.fromDom(node) |
89 |
if node.tagName == 'ParameterSet': |
90 |
parameter_type = node.getAttribute("type") |
91 |
return ParameterSet.fromDom(node) |
92 |
raise "Invalid simulation type, %r" % node.getAttribute("type") |
93 |
|
94 |
|
95 |
raise ValueError("No Simulation Found") |
96 |
|
97 |
|
98 |
class Link: |
99 |
""" |
100 |
a Link makes an attribute of an object callable: |
101 |
o.object() |
102 |
o.a=8 |
103 |
l=Link(o,"a") |
104 |
assert l()==8 |
105 |
""" |
106 |
|
107 |
def __init__(self,target,attribute=None): |
108 |
""" |
109 |
creates a link to the object target. If attribute is given, the link is |
110 |
establised to this attribute of the target. Otherwise the attribute is |
111 |
undefined. |
112 |
""" |
113 |
self.target = target |
114 |
self.attribute = None |
115 |
self.setAttributeName(attribute) |
116 |
|
117 |
def setAttributeName(self,attribute): |
118 |
""" |
119 |
set a new attribute name to be collected from the target object. The |
120 |
target object must have the attribute with name attribute. |
121 |
""" |
122 |
if attribute and self.target: |
123 |
if isinstance(self.target,LinkableObject): |
124 |
if not self.target.hasAttribute(attribute): |
125 |
raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute)) |
126 |
else: |
127 |
if not hasattr(self.target,attribute): |
128 |
raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute)) |
129 |
self.attribute = attribute |
130 |
|
131 |
def hasDefinedAttributeName(self): |
132 |
""" |
133 |
returns true if an attribute name is set |
134 |
""" |
135 |
return self.attribute != None |
136 |
|
137 |
def __repr__(self): |
138 |
""" |
139 |
returns a string representation of the link |
140 |
""" |
141 |
if self.hasDefinedAttributeName(): |
142 |
return "<Link to attribute %s of %s>" % (self.attribute, self.target) |
143 |
else: |
144 |
return "<Link to target %s>" % self.target |
145 |
|
146 |
def __call__(self,name=None): |
147 |
""" |
148 |
returns the value of the attribute of the target object. If the |
149 |
atrribute is callable then the return value of the call is returned. |
150 |
""" |
151 |
if name: |
152 |
out=getattr(self.target, name) |
153 |
else: |
154 |
out=getattr(self.target, self.attribute) |
155 |
|
156 |
if callable(out): |
157 |
return out() |
158 |
else: |
159 |
return out |
160 |
|
161 |
def toDom(self, document, node): |
162 |
""" |
163 |
toDom method of Link. Creates a Link node and appends it to the current XML |
164 |
document |
165 |
""" |
166 |
link = document.createElement('Link') |
167 |
assert (self.target != None), ("Target was none, name was %r" % self.attribute) |
168 |
link.appendChild(dataNode(document, 'Target', self.target.id)) |
169 |
# this use of id will not work for purposes of being able to retrieve the intended |
170 |
# target from the xml later. I need a better unique identifier. |
171 |
assert self.attribute, "You can't xmlify a Link without a target attribute" |
172 |
link.appendChild(dataNode(document, 'Attribute', self.attribute)) |
173 |
node.appendChild(link) |
174 |
|
175 |
def fromDom(cls, doc): |
176 |
targetid = doc.getElementsByTagName("Target")[0].firstChild.nodeValue.strip() |
177 |
attribute = doc.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip() |
178 |
l = cls(None, attribute) |
179 |
registerLink(targetid, l) |
180 |
return l |
181 |
|
182 |
fromDom = classmethod(fromDom) |
183 |
|
184 |
def writeXML(self,ostream=stdout): |
185 |
""" |
186 |
writes an XML representation of self to the output stream ostream. |
187 |
If ostream is nor present the standart output stream is used. If |
188 |
esysheader==True the esys XML header is written |
189 |
""" |
190 |
print 'I got to the Link writeXML method' |
191 |
document, rootnode = esysDoc() |
192 |
self.toDom(document, rootnode) |
193 |
|
194 |
ostream.write(document.toprettyxml()) |
195 |
|
196 |
class LinkableObject(object): |
197 |
""" |
198 |
An object that allows to link its attributes to attributes of other objects |
199 |
via a Link object. For instance |
200 |
|
201 |
p = LinkableObject() |
202 |
p.x = Link(o,"name") |
203 |
print p.x |
204 |
|
205 |
links attribute x of p to the attribute name of object o. |
206 |
|
207 |
p.x will contain the current value of attribute name of object o. |
208 |
|
209 |
If the value of getattr(o, "name") is callable, p.x will rturn the return |
210 |
value of the call. |
211 |
""" |
212 |
|
213 |
number_sequence = itertools.count(100) |
214 |
|
215 |
def __init__(self, debug=False): |
216 |
""" initializes LinkableObject so that we can operate on Links """ |
217 |
self.debug = debug |
218 |
self.__linked_attributes={} |
219 |
self.id = self.number_sequence.next() |
220 |
|
221 |
def trace(self, msg): |
222 |
""" If debugging is on, print the message, otherwise do nothing |
223 |
""" |
224 |
if self.debug: |
225 |
print "%s: %s"%(str(self),msg) |
226 |
|
227 |
def __getattr__(self,name): |
228 |
"""returns the value of attribute name. If the value is a Link object the |
229 |
object is called and the return value is returned.""" |
230 |
out = self.getAttributeObject(name) |
231 |
if isinstance(out,Link): |
232 |
return out() |
233 |
else: |
234 |
return out |
235 |
|
236 |
def getAttributeObject(self,name): |
237 |
"""return the object stored for attribute name.""" |
238 |
|
239 |
if self.__dict__.has_key(name): |
240 |
return self.__dict__[name] |
241 |
|
242 |
if self.__linked_attributes.has_key(name): |
243 |
return self.__linked_attributes[name] |
244 |
|
245 |
if self.__class__.__dict__.has_key(name): |
246 |
return self.__class.__dict__[name] |
247 |
|
248 |
raise AttributeError,"No attribute %s."%name |
249 |
|
250 |
def hasAttribute(self,name): |
251 |
"""returns True if self as attribute name""" |
252 |
return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name) |
253 |
|
254 |
def __setattr__(self,name,value): |
255 |
"""sets the value for attribute name. If value is a Link the target |
256 |
attribute is set to name if no attribute has been specified.""" |
257 |
|
258 |
|
259 |
if self.__dict__.has_key(name): |
260 |
del self.__dict__[name] |
261 |
|
262 |
if isinstance(value,Link): |
263 |
if not value.hasDefinedAttributeName(): |
264 |
value.setAttributeName(name) |
265 |
self.__linked_attributes[name] = value |
266 |
|
267 |
self.trace("attribute %s is now linked by %s."%(name,value)) |
268 |
else: |
269 |
self.__dict__[name] = value |
270 |
|
271 |
def __delattr__(self,name): |
272 |
"""removes the attribute name.""" |
273 |
|
274 |
if self.__linked_attributes.has_key[name]: |
275 |
del self.__linked_attributes[name] |
276 |
elif self.__dict__.has_key(name): |
277 |
del self.__dict__[name] |
278 |
else: |
279 |
raise AttributeError,"No attribute %s."%name |
280 |
|
281 |
class _ParameterIterator: |
282 |
def __init__(self,parameterset): |
283 |
|
284 |
self.__set=parameterset |
285 |
self.__iter=iter(parameterset.parameters) |
286 |
|
287 |
def next(self): |
288 |
o=self.__iter.next() |
289 |
return (o,self.__set.getAttributeObject(o)) |
290 |
|
291 |
def __iter__(self): |
292 |
return self |
293 |
|
294 |
class ParameterSet(LinkableObject): |
295 |
"""a class which allows to emphazise attributes to be written and read to XML |
296 |
|
297 |
Leaves of an ESySParameters objects can be |
298 |
|
299 |
a real number |
300 |
a integer number |
301 |
a string |
302 |
a boolean value |
303 |
a ParameterSet object |
304 |
a Simulation object |
305 |
a Model object |
306 |
any other object (not considered by writeESySXML and writeXML) |
307 |
|
308 |
Example how to create an ESySParameters object: |
309 |
|
310 |
p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.) |
311 |
p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11) |
312 |
parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1"))) |
313 |
|
314 |
This can be accessed as |
315 |
|
316 |
parm.parm1.gamma=0. |
317 |
parm.parm1.dim=2 |
318 |
parm.parm1.tol_v=0.001 |
319 |
parm.parm1.output_file="/tmp/u.%3.3d.dx" |
320 |
parm.parm1.runFlag=True |
321 |
parm.parm1.parm11.gamma1=1. |
322 |
parm.parm1.parm11.gamma2=2. |
323 |
parm.parm1.parm11.gamma3=3. |
324 |
parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1) |
325 |
|
326 |
""" |
327 |
def __init__(self, parameters=[], **kwargs): |
328 |
"""creates a ParameterSet with parameters parameters""" |
329 |
LinkableObject.__init__(self, **kwargs) |
330 |
self.parameters = set() |
331 |
self.declareParameters(parameters) |
332 |
|
333 |
def __repr__(self): |
334 |
return "<%s %r>" % (self.__class__.__name__, |
335 |
[(p, getattr(self, p, None)) for p in self.parameters]) |
336 |
|
337 |
def declareParameter(self,**parameters): |
338 |
"""declares a new parameter(s) and its (their) inital value.""" |
339 |
self.declareParameters(parameters) |
340 |
|
341 |
def declareParameters(self,parameters): |
342 |
"""declares a set of parameters. parameters can be a list, a dictonary or a ParameterSet.""" |
343 |
if isinstance(parameters,ListType): |
344 |
parameters = zip(parameters, itertools.repeat(None)) |
345 |
if isinstance(parameters,DictType): |
346 |
parameters = parameters.iteritems() |
347 |
|
348 |
for prm, value in parameters: |
349 |
setattr(self,prm,value) |
350 |
self.parameters.add(prm) |
351 |
|
352 |
self.trace("parameter %s has been declared."%prm) |
353 |
|
354 |
def releaseParameters(self,name): |
355 |
"""removes parameter name from the paramameters""" |
356 |
if self.isParameter(name): |
357 |
self.parameters.remove(name) |
358 |
self.trace("parameter %s has been removed."%name) |
359 |
|
360 |
def __iter__(self): |
361 |
"""creates an iterator over the parameter and their values""" |
362 |
return _ParameterIterator(self) |
363 |
|
364 |
def showParameters(self): |
365 |
"""returns a descrition of the parameters""" |
366 |
out="{" |
367 |
notfirst=False |
368 |
for i,v in self: |
369 |
if notfirst: out=out+"," |
370 |
notfirst=True |
371 |
if isinstance(v,ParameterSet): |
372 |
out="%s\"%s\" : %s"%(out,i,v.showParameters()) |
373 |
else: |
374 |
out="%s\"%s\" : %s"%(out,i,v) |
375 |
return out+"}" |
376 |
|
377 |
def __delattr__(self,name): |
378 |
"""removes the attribute name.""" |
379 |
LinkableObject.__delattr__(self,name) |
380 |
try: |
381 |
self.releaseParameter(name) |
382 |
except: |
383 |
pass |
384 |
|
385 |
def toDom(self, document, node): |
386 |
""" toDom method of ParameterSet class """ |
387 |
pset = document.createElement('ParameterSet') |
388 |
node.appendChild(pset) |
389 |
self._parametersToDom(document, pset) |
390 |
|
391 |
def _parametersToDom(self, document, node): |
392 |
node.setAttribute ('id', str(self.id)) |
393 |
for name,value in self: |
394 |
param = document.createElement('Parameter') |
395 |
param.setAttribute('type', value.__class__.__name__) |
396 |
|
397 |
param.appendChild(dataNode(document, 'Name', name)) |
398 |
|
399 |
val = document.createElement('Value') |
400 |
|
401 |
if isinstance(value,ParameterSet): |
402 |
value.toDom(document, val) |
403 |
param.appendChild(val) |
404 |
elif isinstance(value, Link): |
405 |
value.toDom(document, val) |
406 |
param.appendChild(val) |
407 |
elif isinstance(value,StringType): |
408 |
param.appendChild(dataNode(document, 'Value', value)) |
409 |
else: |
410 |
param.appendChild(dataNode(document, 'Value', str(value))) |
411 |
|
412 |
node.appendChild(param) |
413 |
|
414 |
def fromDom(cls, doc): |
415 |
|
416 |
# Define a host of helper functions to assist us. |
417 |
def _children(node): |
418 |
""" |
419 |
Remove the empty nodes from the children of this node |
420 |
""" |
421 |
return [x for x in node.childNodes |
422 |
if not isinstance(x, minidom.Text) or x.nodeValue.strip()] |
423 |
|
424 |
def _floatfromValue(doc): |
425 |
return float(doc.nodeValue.strip()) |
426 |
|
427 |
def _stringfromValue(doc): |
428 |
return str(doc.nodeValue.strip()) |
429 |
|
430 |
def _intfromValue(doc): |
431 |
return int(doc.nodeValue.strip()) |
432 |
|
433 |
def _boolfromValue(doc): |
434 |
return bool(doc.nodeValue.strip()) |
435 |
|
436 |
# Mapping from text types in the xml to methods used to process trees of that type |
437 |
ptypemap = {"Simulation": Simulation.fromDom, |
438 |
"Model":Model.fromDom, |
439 |
"ParameterSet":ParameterSet.fromDom, |
440 |
"Link":Link.fromDom, |
441 |
"float":_floatfromValue, |
442 |
"int":_intfromValue, |
443 |
"str":_stringfromValue, |
444 |
"bool":_boolfromValue |
445 |
} |
446 |
|
447 |
# print doc.toxml() |
448 |
|
449 |
parameters = {} |
450 |
for node in _children(doc): |
451 |
ptype = node.getAttribute("type") |
452 |
|
453 |
pname = pvalue = None |
454 |
for childnode in _children(node): |
455 |
|
456 |
if childnode.tagName == "Name": |
457 |
pname = childnode.firstChild.nodeValue.strip() |
458 |
|
459 |
if childnode.tagName == "Value": |
460 |
nodes = _children(childnode) |
461 |
pvalue = ptypemap[ptype](nodes[0]) |
462 |
|
463 |
parameters[pname] = pvalue |
464 |
|
465 |
# Create the instance of ParameterSet |
466 |
o = cls() |
467 |
o.declareParameters(parameters) |
468 |
registerLinkableObject(doc.getAttribute("id"), o) |
469 |
return o |
470 |
|
471 |
fromDom = classmethod(fromDom) |
472 |
|
473 |
def writeXML(self,ostream=stdout): |
474 |
"""writes the object as an XML object into an output stream""" |
475 |
# ParameterSet(d) with d[Name]=Value |
476 |
document, node = esysDoc() |
477 |
self.toDom(document, node) |
478 |
ostream.write(document.toprettyxml()) |
479 |
|
480 |
class Model(ParameterSet): |
481 |
""" |
482 |
|
483 |
A Model object represents a processess marching over time |
484 |
until a finalizing condition is fullfilled. At each time step an iterative |
485 |
process can be performed and the time step size can be controlled. A Model has |
486 |
the following work flow: |
487 |
|
488 |
doInitialization() |
489 |
while not finalize(): |
490 |
dt=getSafeTimeStepSize(dt) |
491 |
doStepPreprocessing(dt) |
492 |
while not terminateIteration(): doStep(dt) |
493 |
doStepPostprocessing(dt) |
494 |
doFinalization() |
495 |
|
496 |
where doInitialization,finalize, getSafeTimeStepSize, doStepPreprocessing, terminateIteration, doStepPostprocessing, doFinalization |
497 |
are methods of the particular instance of a Model. The default implementations of these methods have to be overwritten by |
498 |
the subclass implementinf a Model. |
499 |
|
500 |
""" |
501 |
|
502 |
UNDEF_DT=1.e300 |
503 |
|
504 |
def __init__(self,parameters=[],**kwarg): |
505 |
"""creates a model |
506 |
|
507 |
Just calls the parent constructor. |
508 |
""" |
509 |
ParameterSet.__init__(self, parameters=parameters,**kwarg) |
510 |
|
511 |
def __str__(self): |
512 |
return "<%s %d>"%(self.__class__,id(self)) |
513 |
|
514 |
def toDom(self, document, node): |
515 |
""" toDom method of Model class """ |
516 |
pset = document.createElement('Model') |
517 |
pset.setAttribute('type', self.__class__.__name__) |
518 |
node.appendChild(pset) |
519 |
self._parametersToDom(document, pset) |
520 |
|
521 |
def doInitialization(self): |
522 |
"""initializes the time stepping scheme. This function may be overwritten.""" |
523 |
pass |
524 |
|
525 |
def getSafeTimeStepSize(self,dt): |
526 |
"""returns a time step size which can safely be used. |
527 |
dt gives the previously used step size. |
528 |
This function may be overwritten.""" |
529 |
return self.UNDEF_DT |
530 |
|
531 |
def finalize(self): |
532 |
"""returns False if the time stepping is finalized. This function may be |
533 |
overwritten.""" |
534 |
return False |
535 |
|
536 |
def doFinalization(self): |
537 |
"""finalizes the time stepping. This function may be overwritten.""" |
538 |
pass |
539 |
|
540 |
def doStepPreprocessing(self,dt): |
541 |
"""sets up a time step of step size dt. This function may be overwritten.""" |
542 |
pass |
543 |
|
544 |
def doStep(self,dt): |
545 |
"""executes an iteration step at a time step. |
546 |
dt is the currently used time step size. |
547 |
This function may be overwritten.""" |
548 |
pass |
549 |
|
550 |
def terminateIteration(self): |
551 |
"""returns True if iteration on a time step is terminated.""" |
552 |
return True |
553 |
|
554 |
def doStepPostprocessing(self,dt): |
555 |
"""finalalizes the time step. |
556 |
dt is the currently used time step size. |
557 |
This function may be overwritten.""" |
558 |
pass |
559 |
|
560 |
def writeXML(self, ostream=stdout): |
561 |
document, node = esysDoc() |
562 |
self.toDom(document, node) |
563 |
ostream.write(document.toprettyxml()) |
564 |
|
565 |
|
566 |
|
567 |
class Simulation(Model): |
568 |
""" |
569 |
A Simulation object is special Model which runs a sequence of Models. |
570 |
|
571 |
The methods doInitialization,finalize, getSafeTimeStepSize, doStepPreprocessing, |
572 |
terminateIteration, doStepPostprocessing, doFinalization |
573 |
are executing the corresponding methods of the models in the simulation. |
574 |
|
575 |
""" |
576 |
|
577 |
FAILED_TIME_STEPS_MAX=20 |
578 |
MAX_ITER_STEPS=50 |
579 |
|
580 |
def __init__(self, models=[], **kwargs): |
581 |
"""initiates a simulation from a list of models. """ |
582 |
Model.__init__(self, **kwargs) |
583 |
self.__models=[] |
584 |
|
585 |
for i in range(len(models)): |
586 |
self[i] = models[i] |
587 |
|
588 |
def __repr__(self): |
589 |
""" |
590 |
returns a string representation of the Simulation |
591 |
""" |
592 |
return "<Simulation %r>" % self.__models |
593 |
|
594 |
def __str__(self): |
595 |
""" |
596 |
returning Simulation as a string |
597 |
""" |
598 |
return "<Simulation %d>"%id(self) |
599 |
|
600 |
def iterModels(self): |
601 |
"""returns an iterator over the models""" |
602 |
return self.__models |
603 |
|
604 |
def __getitem__(self,i): |
605 |
"""returns the i-th model""" |
606 |
return self.__models[i] |
607 |
|
608 |
def __setitem__(self,i,value): |
609 |
"""sets the i-th model""" |
610 |
if not isinstance(value,Model): |
611 |
raise ValueError("assigned value is not a Model") |
612 |
for j in range(max(i-len(self.__models)+1,0)): self.__models.append(None) |
613 |
self.__models[i]=value |
614 |
|
615 |
def __len__(self): |
616 |
"""returns the number of models""" |
617 |
return len(self.__models) |
618 |
|
619 |
def toDom(self, document, node): |
620 |
""" toDom method of Simulation class """ |
621 |
simulation = document.createElement('Simulation') |
622 |
simulation.setAttribute('type', self.__class__.__name__) |
623 |
|
624 |
for rank, sim in enumerate(self.iterModels()): |
625 |
component = document.createElement('Component') |
626 |
component.setAttribute('rank', str(rank)) |
627 |
|
628 |
sim.toDom(document, component) |
629 |
|
630 |
simulation.appendChild(component) |
631 |
|
632 |
node.appendChild(simulation) |
633 |
|
634 |
def writeXML(self,ostream=stdout): |
635 |
"""writes the object as an XML object into an output stream""" |
636 |
document, rootnode = esysDoc() |
637 |
self.toDom(document, rootnode) |
638 |
ostream.write(document.toprettyxml()) |
639 |
|
640 |
def getSafeTimeStepSize(self,dt): |
641 |
"""returns a time step size which can safely be used by all models. |
642 |
This is the minimum over the time step sizes of all models.""" |
643 |
out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()]) |
644 |
print "%s: safe step size is %e."%(str(self),out) |
645 |
return out |
646 |
|
647 |
def doInitialization(self): |
648 |
"""initializes all models """ |
649 |
self.n=0 |
650 |
self.tn=0. |
651 |
for o in self.iterModels(): |
652 |
o.doInitialization() |
653 |
|
654 |
def finalize(self): |
655 |
"""returns True if any of the models is to be finalized""" |
656 |
return any([o.finalize() for o in self.iterModels()]) |
657 |
|
658 |
def doFinalization(self): |
659 |
"""finalalizes the time stepping for all models.""" |
660 |
for i in self.iterModels(): i.doFinalization() |
661 |
self.trace("end of time integation.") |
662 |
|
663 |
def doStepPreprocessing(self,dt): |
664 |
"""initializes the time step for all models.""" |
665 |
for o in self.iterModels(): |
666 |
o.doStepPreprocessing(dt) |
667 |
|
668 |
def terminateIteration(self): |
669 |
"""returns True if all iterations for all models are terminated.""" |
670 |
out=all([o.terminateIteration() for o in self.iterModels()]) |
671 |
return out |
672 |
|
673 |
def doStepPostprocessing(self,dt): |
674 |
"""finalalizes the iteration process for all models.""" |
675 |
for o in self.iterModels(): |
676 |
o.doStepPostprocessing(dt) |
677 |
self.n+=1 |
678 |
self.tn+=dt |
679 |
|
680 |
def doStep(self,dt): |
681 |
""" |
682 |
executes the iteration step at a time step for all model: |
683 |
|
684 |
self.doStepPreprocessing(dt) |
685 |
while not self.terminateIteration(): for all models: self.doStep(dt) |
686 |
self.doStepPostprocessing(dt) |
687 |
|
688 |
""" |
689 |
self.iter=0 |
690 |
while not self.terminateIteration(): |
691 |
if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt)) |
692 |
self.iter+=1 |
693 |
self.trace("iteration step %d"%(self.iter)) |
694 |
for o in self.iterModels(): |
695 |
o.doStep(dt) |
696 |
if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt)) |
697 |
|
698 |
def run(self,check_point=None): |
699 |
""" |
700 |
|
701 |
run the simulation by performing essentially |
702 |
|
703 |
self.doInitialization() |
704 |
while not self.finalize(): |
705 |
dt=self.getSafeTimeStepSize() |
706 |
self.doStep(dt) |
707 |
if n%check_point==0: self.writeXML() |
708 |
self.doFinalization() |
709 |
|
710 |
If one of the models in throws a FailedTimeStepError exception a new time step size is |
711 |
computed through getSafeTimeStepSize() and the time step is repeated. |
712 |
|
713 |
If one of the models in throws a IterationDivergenceError exception the time step size |
714 |
is halved and the time step is repeated. |
715 |
|
716 |
In both cases the time integration is given up after Simulation.FAILED_TIME_STEPS_MAX |
717 |
attempts. |
718 |
|
719 |
|
720 |
""" |
721 |
dt=self.UNDEF_DT |
722 |
self.doInitialization() |
723 |
while not self.finalize(): |
724 |
step_fail_counter=0 |
725 |
iteration_fail_counter=0 |
726 |
dt_new=self.getSafeTimeStepSize(dt) |
727 |
self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new)) |
728 |
end_of_step=False |
729 |
while not end_of_step: |
730 |
end_of_step=True |
731 |
if not dt_new>0: |
732 |
raise NonPositiveStepSizeError("non-positive step size in step %d",self.n+1) |
733 |
try: |
734 |
self.doStepPreprocessing(dt_new) |
735 |
self.doStep(dt_new) |
736 |
self.doStepPostprocessing(dt_new) |
737 |
except IterationDivergenceError: |
738 |
dt_new*=0.5 |
739 |
end_of_step=False |
740 |
iteration_fail_counter+=1 |
741 |
if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX: |
742 |
raise SimulationBreakDownError("reduction of time step to achieve convergence failed.") |
743 |
self.trace("iteration fails. time step is repeated with new step size.") |
744 |
except FailedTimeStepError: |
745 |
dt_new=self.getSafeTimeStepSize(dt) |
746 |
end_of_step=False |
747 |
step_fail_counter+=1 |
748 |
self.trace("time step is repeated.") |
749 |
if step_fail_counter>self.FAILED_TIME_STEPS_MAX: |
750 |
raise SimulationBreakDownError("time integration is given up after %d attempts."%step_fail_counter) |
751 |
dt=dt_new |
752 |
if not check_point==None: |
753 |
if n%check_point==0: |
754 |
self.trace("check point is created.") |
755 |
self.writeXML() |
756 |
self.doFinalization() |
757 |
|
758 |
def fromDom(cls, doc): |
759 |
sims = [] |
760 |
for node in doc.childNodes: |
761 |
if isinstance(node, minidom.Text): |
762 |
continue |
763 |
|
764 |
sims.append(getComponent(node)) |
765 |
|
766 |
return cls(sims) |
767 |
|
768 |
fromDom = classmethod(fromDom) |
769 |
|
770 |
|
771 |
class IterationDivergenceError(Exception): |
772 |
""" |
773 |
excpetion which is thrown if there is no convergence of the iteration process at a time step |
774 |
but there is a chance taht a smaller step could help to reach convergence. |
775 |
|
776 |
""" |
777 |
pass |
778 |
|
779 |
class FailedTimeStepError(Exception): |
780 |
"""excpetion which is thrown if the time step fails because of a step size that have been choosen to be too large""" |
781 |
pass |
782 |
|
783 |
class SimulationBreakDownError(Exception): |
784 |
"""excpetion which is thrown if the simulation does not manage to progress in time.""" |
785 |
pass |
786 |
|
787 |
class NonPositiveStepSizeError(Exception): |
788 |
"""excpetion which is thrown if the step size is not positive""" |
789 |
pass |