1 |
# $Id$ |
# $Id$ |
|
from types import StringType |
|
2 |
|
|
3 |
class Link: |
from types import StringType,IntType,FloatType,BooleanType,ListType,DictType |
4 |
""" """ |
from sys import stdout |
5 |
def __init__(self,object,attribute=None): |
import itertools |
6 |
self.__object=object |
# import modellib temporarily removed!!! |
7 |
self.setAttributeName(attribute) |
|
8 |
|
# import the 'set' module if it's not defined (python2.3/2.4 difference) |
9 |
def setAttributeName(self,name): |
try: |
10 |
if not name==None: |
set |
11 |
if not hasattr(self.__object,name): |
except NameError: |
12 |
raise AttributeError("Link: object %s has no attribute %s."%(self.__object,name)) |
from sets import Set as set |
13 |
self.__attribute=name |
|
14 |
|
from xml.dom import minidom |
15 |
def hasAttributeName(self): |
|
16 |
if self.__attribute==None: |
def dataNode(document, tagName, data): |
17 |
return False |
""" |
18 |
else: |
C{dataNode}s are the building blocks of the xml documents constructed in |
19 |
return True |
this module. |
20 |
|
|
21 |
def __str__(self): |
@param document: the current xml document |
22 |
if self.hasAttributeName(): |
@param tagName: the associated xml tag |
23 |
return "reference to %s of %s"%(self.__attribute,self.__object) |
@param data: the values in the tag |
24 |
else: |
""" |
25 |
return "reference to object %s"%self.__object |
t = document.createTextNode(str(data)) |
26 |
|
n = document.createElement(tagName) |
27 |
def getValue(self,name=None): |
n.appendChild(t) |
28 |
if not self.hasAttributeName(): |
return n |
29 |
out=getattr(self.__object,name) |
|
30 |
else: |
def esysDoc(): |
31 |
out=getattr(self.__object,self.__attribute) |
""" |
32 |
if callable(out): |
Global method for creating an instance of an EsysXML document. |
33 |
return out() |
""" |
34 |
else: |
doc = minidom.Document() |
35 |
return out |
esys = doc.createElement('ESys') |
36 |
|
doc.appendChild(esys) |
37 |
class Model: |
return doc, esys |
38 |
""" the Model class provides a framework to run a time-dependent simulation. A Model has a set of parameter which |
|
39 |
may be fixed or altered by the Model itself or other Models over time. |
def all(seq): |
40 |
|
for x in seq: |
41 |
The parameters of a models are declared at instantion, e.g. |
if not x: |
42 |
|
return False |
43 |
|
return True |
44 |
|
|
45 |
|
def any(seq): |
46 |
|
for x in seq: |
47 |
|
if x: |
48 |
|
return True |
49 |
|
return False |
50 |
|
|
51 |
m=Model({"message" : "none" }) |
LinkableObjectRegistry = {} |
52 |
|
|
53 |
creates a Model with parameters p1 and p2 with inital values 1 and 2. Typically a particular model is defined as a subclass of Model: |
def registerLinkableObject(obj_id, o): |
54 |
|
LinkableObjectRegistry[obj_id] = o |
55 |
|
|
56 |
class Messenger(Model): |
LinkRegistry = [] |
|
def __init__(self): |
|
|
Model.__init__(self,parameters={"message" : "none" }) |
|
57 |
|
|
58 |
m=MyModel() |
def registerLink(obj_id, l): |
59 |
|
LinkRegistry.append((obj_id,l)) |
60 |
|
|
61 |
There are various ways how model parameters can be changed: |
def parse(xml): |
62 |
|
""" |
63 |
|
Generic parse method for EsysXML. Without this, Links don't work. |
64 |
|
""" |
65 |
|
global LinkRegistry, LinkableObjectRegistry |
66 |
|
LinkRegistry = [] |
67 |
|
LinkableObjectRegistry = {} |
68 |
|
|
69 |
1) use object attributes: |
doc = minidom.parseString(xml) |
70 |
|
sim = getComponent(doc.firstChild) |
71 |
|
for obj_id, link in LinkRegistry: |
72 |
|
link.target = LinkableObjectRegistry[obj_id] |
73 |
|
|
74 |
m.message="Hello World!" |
return sim |
75 |
|
|
76 |
2) use setParamter method |
def importName(modulename, name): |
77 |
|
""" Import a named object from a module in the context of this function, |
78 |
|
which means you should use fully qualified module paths. |
79 |
|
|
80 |
|
Return None on failure. |
|
m.setParameters(message="Hello World!") |
|
|
|
|
|
3) or dictonaries |
|
|
|
|
|
d={ message : "Hello World!" } |
|
|
m.setParameters(**d) |
|
|
|
|
|
|
|
|
A model executed buy staring the run method of the model: |
|
|
|
|
|
m=Messenger() |
|
|
m.run() |
|
|
|
|
|
The run methods marches through time. It first calls the |
|
|
doInitialization() method of the Model to set up the process. In each time step the doStep() method is called |
|
|
to get from the current to the next time step. The step size is defined by calling the getSafeTimeStepSize() method. |
|
|
The time integration process is terminated when the finalize() methods return true. Final the doFinalization() method |
|
|
is called to finalize the process. To implement a particular model a subclass |
|
|
of the Model class is defined. The subclass overwrites the default methods of Model. |
|
|
|
|
|
The following class defines a messenger printing in the doStep method what ever the current value of its parameter message is: |
|
|
|
|
|
class Messenger(Model): |
|
|
def __init__(self): |
|
|
Model.__init__(self,parameters={"message" : "none" }) |
|
|
|
|
|
def doInitialization(self): |
|
|
print "I start talking now!" |
|
81 |
|
|
82 |
def doStep(self,t): |
This function from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52241 |
83 |
print "Message (time %e) : %s "%(t,self.message) |
""" |
84 |
|
module = __import__(modulename, globals(), locals(), [name]) |
85 |
def doFinalization(self): |
|
86 |
print "I have no more to say!" |
try: |
87 |
|
return vars(module)[name] |
88 |
If a instance of the Messenger class is run, it will print the initialization and finalization message only. |
except KeyError: |
89 |
This is because the default method for finalize() does always returns True and therefore the transition is |
raise ImportError("Could not import %s from %s" % (name, modulename)) |
90 |
terminated startcht away. |
|
91 |
|
def getComponent(doc): |
92 |
|
""" |
93 |
|
Used to get components of Simualtions, Models. |
94 |
|
""" |
95 |
|
for node in doc.childNodes: |
96 |
|
|
97 |
|
if isinstance(node, minidom.Element): |
98 |
|
if node.tagName == 'Simulation': |
99 |
|
if node.getAttribute("type") == 'Simulation': |
100 |
|
return Simulation.fromDom(node) |
101 |
|
if node.tagName == 'Model': |
102 |
|
if (node.getAttribute("module")): |
103 |
|
model_module = node.getAttribute("module") |
104 |
|
model_type = node.getAttribute("type") |
105 |
|
return importName(model_module, model_type).fromDom(node) |
106 |
|
else: |
107 |
|
model_type = node.getAttribute("type") |
108 |
|
model_subclasses = Model.__subclasses__() |
109 |
|
for model in model_subclasses: |
110 |
|
if model_type == model.__name__: |
111 |
|
return Model.fromDom(node) |
112 |
|
if node.tagName == 'ParameterSet': |
113 |
|
parameter_type = node.getAttribute("type") |
114 |
|
return ParameterSet.fromDom(node) |
115 |
|
raise "Invalid simulation type, %r" % node.getAttribute("type") |
116 |
|
|
|
Following example for solving the ODE using a forward euler scheme: |
|
|
|
|
|
u(t=0)=u0 |
|
|
u_t=a*u**2 for all 0<t<=ten |
|
|
|
|
|
exact solution is given by u(t)=1/(1/u0-a*t) |
|
|
|
|
|
class Ode1(Model): |
|
|
def __init__(self,**args): |
|
|
Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 1. },name="test",debug=True) |
|
|
|
|
|
def doInitialization(self): |
|
|
self._tn=0 |
|
|
|
|
|
def doStep(self,t): |
|
|
self.u=self.u+(t-self._tn)*self.a*self.u**2 |
|
|
self._tn=t |
|
|
|
|
|
def doFinalization(self): |
|
|
print "all done final error = ",abs(self.u-1./(1./3.-self.a*self._tn)) |
|
|
|
|
|
def getSafeTimeStepSize(self): |
|
|
return self.dt |
|
117 |
|
|
118 |
def finalize(self): |
raise ValueError("No Simulation Found") |
119 |
return self._tn>=self.tend |
|
120 |
|
|
121 |
In some cases at a given time step an iteration process has to be performed to get the state of the Model for the next time step. ` |
class Link: |
122 |
In this case the doStep() method is replaced by a sequance of methods which implements this iterative process. |
""" |
123 |
The method then will control the iteration process by initializing the iteration through calling the |
A Link makes an attribute of an object callable:: |
|
doIterationInitialization() method. The iteration is preformed by calling the doIterationStep() method until |
|
|
the terminate() method returns True. The doIterationFinalization() method is called to end the iteration. |
|
|
For a particular model these methods have to overwritten by a suitable subclass without touching the doStep() method. |
|
124 |
|
|
125 |
following example is a modification of the example above. Here an implicit euler scheme is used. in each time step the problem |
o.object() |
126 |
|
o.a=8 |
127 |
|
l=Link(o,"a") |
128 |
|
assert l()==8 |
129 |
|
""" |
130 |
|
|
131 |
|
def __init__(self,target,attribute=None): |
132 |
|
""" |
133 |
|
Creates a link to the object target. If attribute is given, the link is |
134 |
|
establised to this attribute of the target. Otherwise the attribute is |
135 |
|
undefined. |
136 |
|
""" |
137 |
|
self.target = target |
138 |
|
self.attribute = None |
139 |
|
self.setAttributeName(attribute) |
140 |
|
|
141 |
|
def setAttributeName(self,attribute): |
142 |
|
""" |
143 |
|
Set a new attribute name to be collected from the target object. The |
144 |
|
target object must have the attribute with name attribute. |
145 |
|
""" |
146 |
|
if attribute and self.target: |
147 |
|
if isinstance(self.target,LinkableObject): |
148 |
|
if not self.target.hasAttribute(attribute): |
149 |
|
raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute)) |
150 |
|
else: |
151 |
|
if not hasattr(self.target,attribute): |
152 |
|
raise AttributeError("%s: target %s has no attribute %s."%(self, self.target, attribute)) |
153 |
|
self.attribute = attribute |
154 |
|
|
155 |
|
def hasDefinedAttributeName(self): |
156 |
|
""" |
157 |
|
Returns true if an attribute name is set. |
158 |
|
""" |
159 |
|
return self.attribute != None |
160 |
|
|
161 |
|
def __repr__(self): |
162 |
|
""" |
163 |
|
Returns a string representation of the link. |
164 |
|
""" |
165 |
|
if self.hasDefinedAttributeName(): |
166 |
|
return "<Link to attribute %s of %s>" % (self.attribute, self.target) |
167 |
|
else: |
168 |
|
return "<Link to target %s>" % self.target |
169 |
|
|
170 |
|
def __call__(self,name=None): |
171 |
|
""" |
172 |
|
Returns the value of the attribute of the target object. If the |
173 |
|
atrribute is callable then the return value of the call is returned. |
174 |
|
""" |
175 |
|
if name: |
176 |
|
out=getattr(self.target, name) |
177 |
|
else: |
178 |
|
out=getattr(self.target, self.attribute) |
179 |
|
|
180 |
|
if callable(out): |
181 |
|
return out() |
182 |
|
else: |
183 |
|
return out |
184 |
|
|
185 |
|
def toDom(self, document, node): |
186 |
|
""" |
187 |
|
C{toDom} method of Link. Creates a Link node and appends it to the |
188 |
|
current XML document. |
189 |
|
""" |
190 |
|
link = document.createElement('Link') |
191 |
|
assert (self.target != None), ("Target was none, name was %r" % self.attribute) |
192 |
|
link.appendChild(dataNode(document, 'Target', self.target.id)) |
193 |
|
# this use of id will not work for purposes of being able to retrieve the intended |
194 |
|
# target from the xml later. I need a better unique identifier. |
195 |
|
assert self.attribute, "You can't xmlify a Link without a target attribute" |
196 |
|
link.appendChild(dataNode(document, 'Attribute', self.attribute)) |
197 |
|
node.appendChild(link) |
198 |
|
|
199 |
|
def fromDom(cls, doc): |
200 |
|
targetid = doc.getElementsByTagName("Target")[0].firstChild.nodeValue.strip() |
201 |
|
attribute = doc.getElementsByTagName("Attribute")[0].firstChild.nodeValue.strip() |
202 |
|
l = cls(None, attribute) |
203 |
|
registerLink(targetid, l) |
204 |
|
return l |
205 |
|
|
206 |
|
fromDom = classmethod(fromDom) |
207 |
|
|
208 |
|
def writeXML(self,ostream=stdout): |
209 |
|
""" |
210 |
|
Writes an XML representation of self to the output stream ostream. |
211 |
|
If ostream is nor present the standart output stream is used. If |
212 |
|
esysheader==True the esys XML header is written |
213 |
|
""" |
214 |
|
print 'I got to the Link writeXML method' |
215 |
|
document, rootnode = esysDoc() |
216 |
|
self.toDom(document, rootnode) |
217 |
|
|
218 |
|
ostream.write(document.toprettyxml()) |
219 |
|
|
220 |
|
class LinkableObject(object): |
221 |
|
""" |
222 |
|
An object that allows to link its attributes to attributes of other objects |
223 |
|
via a Link object. For instance:: |
224 |
|
|
225 |
0= u_{n+1}-u_{n}+a*dt*u_{n+1}**2 |
p = LinkableObject() |
226 |
|
p.x = Link(o,"name") |
227 |
has to be solved for u_{n+1}. The Newton scheme is used to solve this non-linear problem. |
print p.x |
228 |
|
|
229 |
|
links attribute C{x} of C{p} to the attribute name of object C{o}. |
230 |
class Ode2(Model): |
|
231 |
|
C{p.x} will contain the current value of attribute C{name} of object |
232 |
def __init__(self,**args): |
C{o}. |
233 |
Model.__init__(self,{"tend" : 1., "dt" : 0.1 ,"a" : 10. ,"u" : 1. , "tol " : 1.e-8},"test","bla",None,True) |
|
234 |
|
If the value of C{getattr(o, "name")} is callable, C{p.x} will return |
235 |
def doInitialization(self): |
the return value of the call. |
236 |
self.__tn=0 |
""" |
237 |
|
|
238 |
def doIterationInitialization(self,t): |
number_sequence = itertools.count(100) |
239 |
self.__iter=0 |
|
240 |
self.u_last=self.u |
def __init__(self, debug=False): |
241 |
self.current_dt=t-self.tn |
""" |
242 |
self.__tn=t |
Initializes LinkableObject so that we can operate on Links |
243 |
|
""" |
244 |
def doIterationStep(self): |
self.debug = debug |
245 |
self.__iter+=1 |
self.__linked_attributes={} |
246 |
self.u_old=self.u |
self.id = self.number_sequence.next() |
247 |
self.u=(self.current_dt*self.a*self.u**2-self.u_last)/(2*self.current_dt*self.a*self.u-1.) |
registerLinkableObject(self.id, self) |
248 |
|
|
249 |
def terminate(self): |
def trace(self, msg): |
250 |
return abs(self.u_old-self.u)<self.tol*abs(self.u) |
""" |
251 |
|
If debugging is on, print the message, otherwise do nothing |
252 |
def doIterationFinalization(self) |
""" |
253 |
print "all done" |
if self.debug: |
254 |
|
print "%s: %s"%(str(self),msg) |
255 |
def getSafeTimeStepSize(self): |
|
256 |
return self.dt |
def __getattr__(self,name): |
257 |
|
""" |
258 |
def finalize(self): |
Returns the value of attribute name. If the value is a Link object the |
259 |
return self.__tn>self.tend |
object is called and the return value is returned. |
260 |
|
""" |
261 |
A model can be composed from submodels. Submodels are treated as model parameters. If a model parameter is set or a value of |
out = self.getAttributeObject(name) |
262 |
a model parameter is requested, the model will search for this parameter its submodels in the case the model does not have this |
if isinstance(out,Link): |
263 |
parameter itself. The order in which the submodels are searched is critical. By default a Model initializes all its submodels, |
return out() |
264 |
is finalized when all its submodels are finalized and finalizes all its submodels. In the case an iterative process is applied |
else: |
265 |
on a particular time step the iteration is initialized for all submodels, then the iteration step is performed for each submodel |
return out |
266 |
until all submodels indicate termination. Then the iteration is finalized for all submodels. Finally teh doStop() method for all |
|
267 |
submethods is called. |
def getAttributeObject(self,name): |
268 |
|
""" |
269 |
Here we are creating a model which groups ab instantiation of the Ode2 and the Messenger Model |
Return the object stored for attribute name. |
270 |
|
""" |
271 |
o=Ode2() |
|
272 |
m=Messenger() |
if self.__dict__.has_key(name): |
273 |
om=Model(submodels=[o,m],debug=True) |
return self.__dict__[name] |
274 |
om.dt=0.01 |
|
275 |
om.u=1. |
if self.__linked_attributes.has_key(name): |
276 |
m.message="it's me!" |
return self.__linked_attributes[name] |
277 |
om.run() |
|
278 |
|
if self.__class__.__dict__.has_key(name): |
279 |
Notice that dt and u are parameters of class Ode2 and message is a parameter of the Messenger class. The Model formed from these models |
return self.__class.__dict__[name] |
280 |
automatically hand the assignment of new values down to the submodel. om.run() starts this combined model where now the soStep() method |
|
281 |
of the Messenger object printing the value of its parameter message together with a time stamp is executed in each time step introduced |
raise AttributeError,"No attribute %s."%name |
282 |
by the Ode2 model. |
|
283 |
|
def hasAttribute(self,name): |
284 |
A parameter of a Model can be linked to an attribute of onother object, typically an parameter of another Model object. |
""" |
285 |
|
Returns True if self as attribute name. |
286 |
|
""" |
287 |
|
return self.__dict__.has_key(name) or self.__linked_attributes.has_key(name) or self.__class__.__dict__.has_key(name) |
288 |
|
|
289 |
|
def __setattr__(self,name,value): |
290 |
|
""" |
291 |
|
Sets the value for attribute name. If value is a Link the target |
292 |
|
attribute is set to name if no attribute has been specified. |
293 |
|
""" |
294 |
|
|
295 |
|
if self.__dict__.has_key(name): |
296 |
|
del self.__dict__[name] |
297 |
|
|
298 |
|
if isinstance(value,Link): |
299 |
|
if not value.hasDefinedAttributeName(): |
300 |
|
value.setAttributeName(name) |
301 |
|
self.__linked_attributes[name] = value |
302 |
|
|
303 |
|
self.trace("attribute %s is now linked by %s."%(name,value)) |
304 |
|
else: |
305 |
|
self.__dict__[name] = value |
306 |
|
|
307 |
|
def __delattr__(self,name): |
308 |
|
""" |
309 |
|
Removes the attribute name. |
310 |
|
""" |
311 |
|
|
312 |
|
if self.__linked_attributes.has_key[name]: |
313 |
|
del self.__linked_attributes[name] |
314 |
|
elif self.__dict__.has_key(name): |
315 |
|
del self.__dict__[name] |
316 |
|
else: |
317 |
|
raise AttributeError,"No attribute %s."%name |
318 |
|
|
319 |
|
class _ParameterIterator: |
320 |
|
def __init__(self,parameterset): |
321 |
|
|
322 |
|
self.__set=parameterset |
323 |
|
self.__iter=iter(parameterset.parameters) |
324 |
|
|
325 |
|
def next(self): |
326 |
|
o=self.__iter.next() |
327 |
|
return (o,self.__set.getAttributeObject(o)) |
328 |
|
|
329 |
|
def __iter__(self): |
330 |
|
return self |
331 |
|
|
332 |
|
class ParameterSet(LinkableObject): |
333 |
|
""" |
334 |
|
A class which allows to emphazise attributes to be written and read to XML |
335 |
|
|
336 |
which is comprised by a set of submodels. |
Leaves of an ESySParameters object can be: |
337 |
The simulation is run through its run method which in the simplest case has the form: |
|
338 |
|
- a real number |
339 |
|
- a integer number |
340 |
|
- a string |
341 |
|
- a boolean value |
342 |
|
- a ParameterSet object |
343 |
|
- a Simulation object |
344 |
|
- a Model object |
345 |
|
- any other object (not considered by writeESySXML and writeXML) |
346 |
|
|
347 |
|
Example how to create an ESySParameters object:: |
348 |
|
|
349 |
|
p11=ParameterSet(gamma1=1.,gamma2=2.,gamma3=3.) |
350 |
|
p1=ParameterSet(dim=2,tol_v=0.001,output_file="/tmp/u.%3.3d.dx",runFlag=True,parm11=p11) |
351 |
|
parm=ParameterSet(parm1=p1,parm2=ParameterSet(alpha=Link(p11,"gamma1"))) |
352 |
|
|
353 |
|
This can be accessed as:: |
354 |
|
|
355 |
|
parm.parm1.gamma=0. |
356 |
|
parm.parm1.dim=2 |
357 |
|
parm.parm1.tol_v=0.001 |
358 |
|
parm.parm1.output_file="/tmp/u.%3.3d.dx" |
359 |
|
parm.parm1.runFlag=True |
360 |
|
parm.parm1.parm11.gamma1=1. |
361 |
|
parm.parm1.parm11.gamma2=2. |
362 |
|
parm.parm1.parm11.gamma3=3. |
363 |
|
parm.parm2.alpha=1. (value of parm.parm1.parm11.gamma1) |
364 |
|
""" |
365 |
|
def __init__(self, parameters=[], **kwargs): |
366 |
|
""" |
367 |
|
Creates a ParameterSet with parameters parameters. |
368 |
|
""" |
369 |
|
LinkableObject.__init__(self, **kwargs) |
370 |
|
self.parameters = set() |
371 |
|
self.declareParameters(parameters) |
372 |
|
|
373 |
|
def __repr__(self): |
374 |
|
return "<%s %r>" % (self.__class__.__name__, |
375 |
|
[(p, getattr(self, p, None)) for p in self.parameters]) |
376 |
|
|
377 |
|
def declareParameter(self,**parameters): |
378 |
|
""" |
379 |
|
Declares a new parameter(s) and its (their) initial value. |
380 |
|
""" |
381 |
|
self.declareParameters(parameters) |
382 |
|
|
383 |
|
def declareParameters(self,parameters): |
384 |
|
""" |
385 |
|
Declares a set of parameters. parameters can be a list, a dictionary |
386 |
|
or a ParameterSet. |
387 |
|
""" |
388 |
|
if isinstance(parameters,ListType): |
389 |
|
parameters = zip(parameters, itertools.repeat(None)) |
390 |
|
if isinstance(parameters,DictType): |
391 |
|
parameters = parameters.iteritems() |
392 |
|
|
393 |
|
for prm, value in parameters: |
394 |
|
setattr(self,prm,value) |
395 |
|
self.parameters.add(prm) |
396 |
|
|
397 |
|
self.trace("parameter %s has been declared."%prm) |
398 |
|
|
399 |
|
def releaseParameters(self,name): |
400 |
|
""" |
401 |
|
Removes parameter name from the paramameters. |
402 |
|
""" |
403 |
|
if self.isParameter(name): |
404 |
|
self.parameters.remove(name) |
405 |
|
self.trace("parameter %s has been removed."%name) |
406 |
|
|
407 |
|
def __iter__(self): |
408 |
|
""" |
409 |
|
Creates an iterator over the parameter and their values. |
410 |
|
""" |
411 |
|
return _ParameterIterator(self) |
412 |
|
|
413 |
|
def showParameters(self): |
414 |
|
""" |
415 |
|
Returns a descrition of the parameters. |
416 |
|
""" |
417 |
|
out="{" |
418 |
|
notfirst=False |
419 |
|
for i,v in self: |
420 |
|
if notfirst: out=out+"," |
421 |
|
notfirst=True |
422 |
|
if isinstance(v,ParameterSet): |
423 |
|
out="%s\"%s\" : %s"%(out,i,v.showParameters()) |
424 |
|
else: |
425 |
|
out="%s\"%s\" : %s"%(out,i,v) |
426 |
|
return out+"}" |
427 |
|
|
428 |
|
def __delattr__(self,name): |
429 |
|
""" |
430 |
|
Removes the attribute name. |
431 |
|
""" |
432 |
|
LinkableObject.__delattr__(self,name) |
433 |
|
try: |
434 |
|
self.releaseParameter(name) |
435 |
|
except: |
436 |
|
pass |
437 |
|
|
438 |
|
def toDom(self, document, node): |
439 |
|
""" |
440 |
|
C{toDom} method of ParameterSet class. |
441 |
|
""" |
442 |
|
pset = document.createElement('ParameterSet') |
443 |
|
node.appendChild(pset) |
444 |
|
self._parametersToDom(document, pset) |
445 |
|
|
446 |
|
def _parametersToDom(self, document, node): |
447 |
|
node.setAttribute ('id', str(self.id)) |
448 |
|
for name,value in self: |
449 |
|
param = document.createElement('Parameter') |
450 |
|
param.setAttribute('type', value.__class__.__name__) |
451 |
|
|
452 |
|
param.appendChild(dataNode(document, 'Name', name)) |
453 |
|
|
454 |
|
val = document.createElement('Value') |
455 |
|
|
456 |
|
if isinstance(value,ParameterSet): |
457 |
|
value.toDom(document, val) |
458 |
|
param.appendChild(val) |
459 |
|
elif isinstance(value, Link): |
460 |
|
value.toDom(document, val) |
461 |
|
param.appendChild(val) |
462 |
|
elif isinstance(value,StringType): |
463 |
|
param.appendChild(dataNode(document, 'Value', value)) |
464 |
|
else: |
465 |
|
param.appendChild(dataNode(document, 'Value', str(value))) |
466 |
|
|
467 |
s=Model() |
node.appendChild(param) |
|
s.run() |
|
468 |
|
|
469 |
The run has an initializion and finalization phase. The latter is called if all submodels are to be finalized. The |
def fromDom(cls, doc): |
|
simulation is processing in time through calling the stepForward methods which updates the observables of each submodel. |
|
|
A time steps size which is save for all submodel is choosen. |
|
470 |
|
|
471 |
At given time step an iterative process may be performed to make sure that all observables are consistent across all submodels. |
# Define a host of helper functions to assist us. |
472 |
In this case, similar the time dependence, an initialization and finalization of the iteration is performed. |
def _children(node): |
473 |
|
""" |
474 |
|
Remove the empty nodes from the children of this node. |
475 |
|
""" |
476 |
|
return [x for x in node.childNodes |
477 |
|
if not isinstance(x, minidom.Text) or x.nodeValue.strip()] |
478 |
|
|
479 |
A Model has input and output parameters where each input parameter can be constant, time dependent or may depend on an |
def _floatfromValue(doc): |
480 |
output parameter of another model or the model itself. To create a parameter name of a model and to |
return float(doc.nodeValue.strip()) |
|
assign a value to it one can use the statement |
|
481 |
|
|
482 |
model.name=object |
def _stringfromValue(doc): |
483 |
|
return str(doc.nodeValue.strip()) |
484 |
|
|
485 |
|
def _intfromValue(doc): |
486 |
|
return int(doc.nodeValue.strip()) |
487 |
|
|
488 |
|
def _boolfromValue(doc): |
489 |
|
return bool(doc.nodeValue.strip()) |
490 |
|
|
491 |
At any time the current value of the parameter name can be obtained by |
def _nonefromValue(doc): |
492 |
|
return None |
493 |
|
|
494 |
|
# Mapping from text types in the xml to methods used to process trees of that type |
495 |
|
ptypemap = {"Simulation": Simulation.fromDom, |
496 |
|
"Model":Model.fromDom, |
497 |
|
"ParameterSet":ParameterSet.fromDom, |
498 |
|
"Link":Link.fromDom, |
499 |
|
"float":_floatfromValue, |
500 |
|
"int":_intfromValue, |
501 |
|
"str":_stringfromValue, |
502 |
|
"bool":_boolfromValue |
503 |
|
"NoneType":_nonefromValue, |
504 |
|
} |
505 |
|
|
506 |
|
# print doc.toxml() |
507 |
|
|
508 |
|
parameters = {} |
509 |
|
for node in _children(doc): |
510 |
|
ptype = node.getAttribute("type") |
511 |
|
|
512 |
|
pname = pvalue = None |
513 |
|
for childnode in _children(node): |
514 |
|
|
515 |
|
if childnode.tagName == "Name": |
516 |
|
pname = childnode.firstChild.nodeValue.strip() |
517 |
|
|
518 |
|
if childnode.tagName == "Value": |
519 |
|
nodes = _children(childnode) |
520 |
|
pvalue = ptypemap[ptype](nodes[0]) |
521 |
|
|
522 |
|
parameters[pname] = pvalue |
523 |
|
|
524 |
|
# Create the instance of ParameterSet |
525 |
|
o = cls() |
526 |
|
o.declareParameters(parameters) |
527 |
|
registerLinkableObject(doc.getAttribute("id"), o) |
528 |
|
return o |
529 |
|
|
530 |
|
fromDom = classmethod(fromDom) |
531 |
|
|
532 |
|
def writeXML(self,ostream=stdout): |
533 |
|
""" |
534 |
|
Writes the object as an XML object into an output stream. |
535 |
|
""" |
536 |
|
# ParameterSet(d) with d[Name]=Value |
537 |
|
document, node = esysDoc() |
538 |
|
self.toDom(document, node) |
539 |
|
ostream.write(document.toprettyxml()) |
540 |
|
|
541 |
|
class Model(ParameterSet): |
542 |
|
""" |
543 |
|
A Model object represents a processess marching over time until a |
544 |
|
finalizing condition is fullfilled. At each time step an iterative |
545 |
|
process can be performed and the time step size can be controlled. A |
546 |
|
Model has the following work flow:: |
547 |
|
|
548 |
|
doInitialization() |
549 |
|
while not finalize(): |
550 |
|
dt=getSafeTimeStepSize(dt) |
551 |
|
doStepPreprocessing(dt) |
552 |
|
while not terminateIteration(): doStep(dt) |
553 |
|
doStepPostprocessing(dt) |
554 |
|
doFinalization() |
555 |
|
|
556 |
|
where C{doInitialization}, C{finalize}, C{getSafeTimeStepSize}, |
557 |
|
C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing}, |
558 |
|
C{doFinalization} are methods of the particular instance of a Model. The |
559 |
|
default implementations of these methods have to be overwritten by the |
560 |
|
subclass implementing a Model. |
561 |
|
""" |
562 |
|
|
563 |
|
UNDEF_DT=1.e300 |
564 |
|
|
565 |
|
def __init__(self,parameters=[],**kwarg): |
566 |
|
""" |
567 |
|
Creates a model. |
568 |
|
|
569 |
|
Just calls the parent constructor. |
570 |
|
""" |
571 |
|
ParameterSet.__init__(self, parameters=parameters,**kwarg) |
572 |
|
|
573 |
|
def __str__(self): |
574 |
|
return "<%s %d>"%(self.__class__,id(self)) |
575 |
|
|
576 |
|
def toDom(self, document, node): |
577 |
|
""" |
578 |
|
C{toDom} method of Model class |
579 |
|
""" |
580 |
|
pset = document.createElement('Model') |
581 |
|
pset.setAttribute('type', self.__class__.__name__) |
582 |
|
if not self.__class__.__module__.startswith('esys.escript'): |
583 |
|
pset.setAttribute('module', self.__class__.__module__) |
584 |
|
node.appendChild(pset) |
585 |
|
self._parametersToDom(document, pset) |
586 |
|
|
587 |
|
def doInitialization(self): |
588 |
|
""" |
589 |
|
Initializes the time stepping scheme. |
590 |
|
|
591 |
|
This function may be overwritten. |
592 |
|
""" |
593 |
|
pass |
594 |
|
|
595 |
|
def getSafeTimeStepSize(self,dt): |
596 |
|
""" |
597 |
|
Returns a time step size which can safely be used. |
598 |
|
|
599 |
|
C{dt} gives the previously used step size. |
600 |
|
|
601 |
|
This function may be overwritten. |
602 |
|
""" |
603 |
|
return self.UNDEF_DT |
604 |
|
|
605 |
|
def finalize(self): |
606 |
|
""" |
607 |
|
Returns False if the time stepping is finalized. |
608 |
|
|
609 |
|
This function may be overwritten. |
610 |
|
""" |
611 |
|
return False |
612 |
|
|
613 |
|
def doFinalization(self): |
614 |
|
""" |
615 |
|
Finalizes the time stepping. |
616 |
|
|
617 |
|
This function may be overwritten. |
618 |
|
""" |
619 |
|
pass |
620 |
|
|
621 |
|
def doStepPreprocessing(self,dt): |
622 |
|
""" |
623 |
|
Sets up a time step of step size dt. |
624 |
|
|
625 |
|
This function may be overwritten. |
626 |
|
""" |
627 |
|
pass |
628 |
|
|
629 |
|
def doStep(self,dt): |
630 |
|
""" |
631 |
|
Executes an iteration step at a time step. |
632 |
|
|
633 |
|
C{dt} is the currently used time step size. |
634 |
|
|
635 |
|
This function may be overwritten. |
636 |
|
""" |
637 |
|
pass |
638 |
|
|
639 |
|
def terminateIteration(self): |
640 |
|
""" |
641 |
|
Returns True if iteration on a time step is terminated. |
642 |
|
""" |
643 |
|
return True |
644 |
|
|
645 |
|
def doStepPostprocessing(self,dt): |
646 |
|
""" |
647 |
|
Finalalizes the time step. |
648 |
|
|
649 |
|
dt is the currently used time step size. |
650 |
|
|
651 |
|
This function may be overwritten. |
652 |
|
""" |
653 |
|
pass |
654 |
|
|
655 |
|
def writeXML(self, ostream=stdout): |
656 |
|
document, node = esysDoc() |
657 |
|
self.toDom(document, node) |
658 |
|
ostream.write(document.toprettyxml()) |
659 |
|
|
660 |
|
|
661 |
|
class Simulation(Model): |
662 |
|
""" |
663 |
|
A Simulation object is special Model which runs a sequence of Models. |
664 |
|
|
665 |
|
The methods C{doInitialization}, C{finalize}, C{getSafeTimeStepSize}, |
666 |
|
C{doStepPreprocessing}, C{terminateIteration}, C{doStepPostprocessing}, |
667 |
|
C{doFinalization} are executing the corresponding methods of the models in |
668 |
|
the simulation. |
669 |
|
""" |
670 |
|
|
671 |
|
FAILED_TIME_STEPS_MAX=20 |
672 |
|
MAX_ITER_STEPS=50 |
673 |
|
|
674 |
|
def __init__(self, models=[], **kwargs): |
675 |
|
""" |
676 |
|
Initiates a simulation from a list of models. |
677 |
|
""" |
678 |
|
Model.__init__(self, **kwargs) |
679 |
|
self.__models=[] |
680 |
|
|
681 |
|
for i in range(len(models)): |
682 |
|
self[i] = models[i] |
683 |
|
|
684 |
|
|
685 |
value=model.name |
def __repr__(self): |
686 |
|
""" |
687 |
|
Returns a string representation of the Simulation. |
688 |
|
""" |
689 |
|
return "<Simulation %r>" % self.__models |
690 |
|
|
691 |
|
def __str__(self): |
692 |
|
""" |
693 |
|
Returning Simulation as a string. |
694 |
|
""" |
695 |
|
return "<Simulation %d>"%id(self) |
696 |
|
|
697 |
|
def iterModels(self): |
698 |
|
""" |
699 |
|
Returns an iterator over the models. |
700 |
|
""" |
701 |
|
return self.__models |
702 |
|
|
703 |
|
def __getitem__(self,i): |
704 |
|
""" |
705 |
|
Returns the i-th model. |
706 |
|
""" |
707 |
|
return self.__models[i] |
708 |
|
|
709 |
|
def __setitem__(self,i,value): |
710 |
|
""" |
711 |
|
Sets the i-th model. |
712 |
|
""" |
713 |
|
if not isinstance(value,Model): |
714 |
|
raise ValueError("assigned value is not a Model") |
715 |
|
for j in range(max(i-len(self.__models)+1,0)): |
716 |
|
self.__models.append(None) |
717 |
|
self.__models[i]=value |
718 |
|
|
719 |
|
def __len__(self): |
720 |
|
""" |
721 |
|
Returns the number of models. |
722 |
|
""" |
723 |
|
return len(self.__models) |
724 |
|
|
725 |
|
def toDom(self, document, node): |
726 |
|
""" |
727 |
|
C{toDom} method of Simulation class. |
728 |
|
""" |
729 |
|
simulation = document.createElement('Simulation') |
730 |
|
simulation.setAttribute('type', self.__class__.__name__) |
731 |
|
|
732 |
|
for rank, sim in enumerate(self.iterModels()): |
733 |
|
component = document.createElement('Component') |
734 |
|
component.setAttribute('rank', str(rank)) |
735 |
|
|
736 |
|
sim.toDom(document, component) |
737 |
|
|
738 |
|
simulation.appendChild(component) |
739 |
|
|
740 |
|
node.appendChild(simulation) |
741 |
|
|
742 |
|
def writeXML(self,ostream=stdout): |
743 |
|
""" |
744 |
|
Writes the object as an XML object into an output stream. |
745 |
|
""" |
746 |
|
document, rootnode = esysDoc() |
747 |
|
self.toDom(document, rootnode) |
748 |
|
targetsList = document.getElementsByTagName('Target') |
749 |
|
for i in targetsList: |
750 |
|
targetId = int(i.firstChild.nodeValue.strip()) |
751 |
|
targetObj = LinkableObjectRegistry[targetId] |
752 |
|
targetObj.toDom(document, rootnode) |
753 |
|
ostream.write(document.toprettyxml()) |
754 |
|
|
755 |
|
def getSafeTimeStepSize(self,dt): |
756 |
|
""" |
757 |
|
Returns a time step size which can safely be used by all models. |
758 |
|
|
759 |
|
This is the minimum over the time step sizes of all models. |
760 |
|
""" |
761 |
|
out=min([o.getSafeTimeStepSize(dt) for o in self.iterModels()]) |
762 |
|
#print "%s: safe step size is %e."%(str(self),out) |
763 |
|
return out |
764 |
|
|
765 |
|
def doInitialization(self): |
766 |
|
""" |
767 |
|
Initializes all models. |
768 |
|
""" |
769 |
|
self.n=0 |
770 |
|
self.tn=0. |
771 |
|
for o in self.iterModels(): |
772 |
|
o.doInitialization() |
773 |
|
|
774 |
|
def finalize(self): |
775 |
|
""" |
776 |
|
Returns True if any of the models is to be finalized. |
777 |
|
""" |
778 |
|
return any([o.finalize() for o in self.iterModels()]) |
779 |
|
|
780 |
|
def doFinalization(self): |
781 |
|
""" |
782 |
|
Finalalizes the time stepping for all models. |
783 |
|
""" |
784 |
|
for i in self.iterModels(): i.doFinalization() |
785 |
|
self.trace("end of time integation.") |
786 |
|
|
787 |
|
def doStepPreprocessing(self,dt): |
788 |
|
""" |
789 |
|
Initializes the time step for all models. |
790 |
|
""" |
791 |
|
for o in self.iterModels(): |
792 |
|
o.doStepPreprocessing(dt) |
793 |
|
|
794 |
|
def terminateIteration(self): |
795 |
|
""" |
796 |
|
Returns True if all iterations for all models are terminated. |
797 |
|
""" |
798 |
|
out=all([o.terminateIteration() for o in self.iterModels()]) |
799 |
|
return out |
800 |
|
|
801 |
|
def doStepPostprocessing(self,dt): |
802 |
|
""" |
803 |
|
Finalalizes the iteration process for all models. |
804 |
|
""" |
805 |
|
for o in self.iterModels(): |
806 |
|
o.doStepPostprocessing(dt) |
807 |
|
self.n+=1 |
808 |
|
self.tn+=dt |
809 |
|
|
810 |
|
def doStep(self,dt): |
811 |
|
""" |
812 |
|
Executes the iteration step at a time step for all model:: |
813 |
|
|
814 |
|
self.doStepPreprocessing(dt) |
815 |
|
while not self.terminateIteration(): |
816 |
|
for all models: |
817 |
|
self.doStep(dt) |
818 |
|
self.doStepPostprocessing(dt) |
819 |
|
""" |
820 |
|
self.iter=0 |
821 |
|
while not self.terminateIteration(): |
822 |
|
if self.iter==0: self.trace("iteration at %d-th time step %e starts"%(self.n+1,self.tn+dt)) |
823 |
|
self.iter+=1 |
824 |
|
self.trace("iteration step %d"%(self.iter)) |
825 |
|
for o in self.iterModels(): |
826 |
|
o.doStep(dt) |
827 |
|
if self.iter>0: self.trace("iteration at %d-th time step %e finalized."%(self.n+1,self.tn+dt)) |
828 |
|
|
829 |
|
def run(self,check_point=None): |
830 |
|
""" |
831 |
|
Run the simulation by performing essentially:: |
832 |
|
|
833 |
|
self.doInitialization() |
834 |
|
while not self.finalize(): |
835 |
|
dt=self.getSafeTimeStepSize() |
836 |
|
self.doStep(dt) |
837 |
|
if n%check_point==0: |
838 |
|
self.writeXML() |
839 |
|
self.doFinalization() |
840 |
|
|
841 |
|
If one of the models in throws a C{FailedTimeStepError} exception a |
842 |
|
new time step size is computed through getSafeTimeStepSize() and the |
843 |
|
time step is repeated. |
844 |
|
|
845 |
|
If one of the models in throws a C{IterationDivergenceError} |
846 |
|
exception the time step size is halved and the time step is repeated. |
847 |
|
|
848 |
If the object that has been assigned to the paramter/attribute name has the attribute/parameter name isself the current value of this |
In both cases the time integration is given up after |
849 |
attribute of the object is returned (e.g. for model.name=object where object has an attribute name, the statement value=model.name whould assign |
C{Simulation.FAILED_TIME_STEPS_MAX} attempts. |
850 |
the value object.name to value.). If the name of the parameters of a model and an object don't match the setParameter method of model can be used. So |
""" |
851 |
|
dt=self.UNDEF_DT |
852 |
|
self.doInitialization() |
853 |
|
while not self.finalize(): |
854 |
|
step_fail_counter=0 |
855 |
|
iteration_fail_counter=0 |
856 |
|
dt_new=self.getSafeTimeStepSize(dt) |
857 |
|
self.trace("%d. time step %e (step size %e.)" % (self.n+1,self.tn+dt_new,dt_new)) |
858 |
|
end_of_step=False |
859 |
|
while not end_of_step: |
860 |
|
end_of_step=True |
861 |
|
if not dt_new>0: |
862 |
|
raise NonPositiveStepSizeError("non-positive step size in step %d",self.n+1) |
863 |
|
try: |
864 |
|
self.doStepPreprocessing(dt_new) |
865 |
|
self.doStep(dt_new) |
866 |
|
self.doStepPostprocessing(dt_new) |
867 |
|
except IterationDivergenceError: |
868 |
|
dt_new*=0.5 |
869 |
|
end_of_step=False |
870 |
|
iteration_fail_counter+=1 |
871 |
|
if iteration_fail_counter>self.FAILED_TIME_STEPS_MAX: |
872 |
|
raise SimulationBreakDownError("reduction of time step to achieve convergence failed.") |
873 |
|
self.trace("iteration fails. time step is repeated with new step size.") |
874 |
|
except FailedTimeStepError: |
875 |
|
dt_new=self.getSafeTimeStepSize(dt) |
876 |
|
end_of_step=False |
877 |
|
step_fail_counter+=1 |
878 |
|
self.trace("time step is repeated.") |
879 |
|
if step_fail_counter>self.FAILED_TIME_STEPS_MAX: |
880 |
|
raise SimulationBreakDownError("time integration is given up after %d attempts."%step_fail_counter) |
881 |
|
dt=dt_new |
882 |
|
if not check_point==None: |
883 |
|
if n%check_point==0: |
884 |
|
self.trace("check point is created.") |
885 |
|
self.writeXML() |
886 |
|
self.doFinalization() |
887 |
|
|
888 |
|
def fromDom(cls, doc): |
889 |
|
sims = [] |
890 |
|
for node in doc.childNodes: |
891 |
|
if isinstance(node, minidom.Text): |
892 |
|
continue |
893 |
|
|
894 |
model.setParameter(name,object,name_for_object) |
sims.append(getComponent(node)) |
895 |
|
|
896 |
links the parameter name of model with the parameter name_for_object of object. |
return cls(sims) |
897 |
|
|
898 |
The run method initiates checkpointing (it is not clear how to do this yet) |
fromDom = classmethod(fromDom) |
|
===== |
|
|
|
|
|
""" |
|
|
# step size used in case of an undefined value for the step size |
|
|
UNDEF_DT=1.e300 |
|
|
|
|
|
def __init__(self,submodels=[],parameters={},name="model",description="none",check_pointing=None,debug=False): |
|
|
"""initiates a model from a list of submodels. """ |
|
|
self.setDebug(debug) |
|
|
self.__check_pointing=check_pointing |
|
|
self.__parameters={} |
|
|
self.setName(name) |
|
|
self.setDescription(description) |
|
|
self.declareParameter(**parameters) |
|
|
# get the models defined in parameters: |
|
|
self.__submodels=[] |
|
|
# submodels==None means no submodels used: |
|
|
if submodels==None: |
|
|
pass |
|
|
# no submodel list given means all submodels are used as defined by the parameters dictionary: |
|
|
elif len(submodels)==0: |
|
|
for i in parameters.keys(): |
|
|
if isinstance(parameters[i],Model): self.__submodels.append(i) |
|
|
# submodel list of strings and Models is given, submodels defines the order in which the |
|
|
# submodels are processed. if new models are found in the list they are added to the parameter dictionary. |
|
|
else: |
|
|
c=0 |
|
|
for i in submodels: |
|
|
if isinstance(i,StringType): |
|
|
m=self.getParameter(i) |
|
|
if not isinstance(m,Model): |
|
|
raise ValueError,"submodel %s is not a model."%i |
|
|
else: |
|
|
if not isinstance(i,Model): |
|
|
raise ValueError,"submodel list does contain item which is not a Model class object." |
|
|
m=i |
|
|
i="__submodel%d__"%c |
|
|
self.declareParameter(**{i : m}) |
|
|
c+=1 |
|
|
self.__submodels.append(i) |
|
|
if self.debug(): print "%s: model %s is added as parameter %s."%(self,m,i) |
|
|
if len(self.__submodels)>0 and self.debug(): print "%s: model ordering is %s"%(self,self.__submodels) |
|
|
def setSubmodelOrder(submodels=[]): |
|
|
"""sets a new ordering for submodels""" |
|
|
|
|
|
|
|
|
# |
|
|
# some basic fuctions: |
|
|
# |
|
|
def debugOn(self): |
|
|
"""sets debugging to on""" |
|
|
self.__debug=True |
|
|
def debugOff(self): |
|
|
"""sets debugging to off""" |
|
|
self.__debug=False |
|
|
def debug(self): |
|
|
"""returns True if debug mode is set to on""" |
|
|
return self.__debug |
|
|
def setDebug(self,flag=False): |
|
|
"""sets debugging to flag""" |
|
|
if flag: |
|
|
self.debugOn() |
|
|
else: |
|
|
self.debugOff() |
|
|
def setDebug(self,flag=False): |
|
|
"""sets debugging to flag""" |
|
|
if flag: |
|
|
self.debugOn() |
|
|
else: |
|
|
self.debugOff() |
|
|
# name and description handling |
|
|
def __str__(self): |
|
|
"""returns the name of the model""" |
|
|
return self.getName() |
|
|
|
|
|
def getName(self): |
|
|
"""returns the name of the model""" |
|
|
return self.__name |
|
|
|
|
|
def getFullName(self): |
|
|
"""returns the full name of the model including all the names of the submodels""" |
|
|
out=str(self)+"(" |
|
|
notfirst=False |
|
|
for i in self.__submodels: |
|
|
if notfirst: out=out+"," |
|
|
out=out+i.getFullName() |
|
|
notfirst=True |
|
|
return out+")" |
|
899 |
|
|
|
def setName(self,name): |
|
|
"""sets the name of the model""" |
|
|
self.__name=name |
|
|
|
|
|
def setDescription(self,description="none"): |
|
|
"""sets new description""" |
|
|
self.__description=description |
|
|
if self.debug(): print "%s: description is set to %s."%(self,description) |
|
|
def getDescription(self): |
|
|
"""returns the description of the model""" |
|
|
return self.__description |
|
|
# |
|
|
# parameter/attribute handling: |
|
|
# |
|
|
def declareParameter(self,**parameters): |
|
|
"""declares a new parameter and its inital value.""" |
|
|
for prm in parameters.keys(): |
|
|
if prm in self.__dict__.keys(): |
|
|
raise ValueError,"object attribute %s of %s cannot be used as a model parameter."%(prm,self) |
|
|
self.__parameters[prm]=parameters[prm] |
|
|
if self.debug(): print "%s: parameter %s has been declared."%(self,prm) |
|
|
|
|
|
|
|
|
|
|
|
def showParameters(self): |
|
|
"""returns a descrition of the parameters""" |
|
|
out="" |
|
|
notfirst=False |
|
|
for i in self.__parameters: |
|
|
if notfirst: out=out+"," |
|
|
notfirst=True |
|
|
out="%s%s=%s"%(out,i,self.__parameters[i]) |
|
|
return out |
|
|
|
|
|
|
|
|
def deleteParameter(self,name): |
|
|
"""removes parameter name from the model""" |
|
|
raise IllegalParameterError("Cannot delete parameter %s."%name) |
|
|
|
|
|
def getParameter(self,name): |
|
|
"""returns the value of parameter name. If the parameter is not declared in self, the submodels are searched. |
|
|
if the parameter is a Link, the current value of the obejective is returned.""" |
|
|
if self.__parameters.has_key(name): |
|
|
if isinstance(self.__parameters[name],Link): |
|
|
out=self.__parameters[name].getValue(name) |
|
|
else: |
|
|
out=self.__parameters[name] |
|
|
else: |
|
|
out=None |
|
|
for i in self.__submodels: |
|
|
try: |
|
|
out=self.__parameters[i].getParameter(name) |
|
|
except IllegalParameterError: |
|
|
pass |
|
|
if out==None: raise IllegalParameterError("Cannot find parameter %s."%name) |
|
|
return out |
|
|
|
|
|
def setParameter(self,**parameters): |
|
|
"""sets parameter name to value. If the initial value for the parameter is a Model, the new value has to be a Model.""" |
|
|
for name in parameters.keys(): |
|
|
if self.__parameters.has_key(name): |
|
|
if not isinstance(parameters[name],Model) and isinstance(self.__parameters[name],Model): |
|
|
raise ValueError,"%s: parameter %s can assigned to a Model object only."%(self,name) |
|
|
if isinstance(parameters[name],Model) and not isinstance(self.__parameters[name],Model): |
|
|
raise ValueError,"%s: parameter %s is not declared as a Model."%(self,name) |
|
|
self.__parameters[name]=parameters[name] |
|
|
if isinstance(self.__parameters[name],Link): |
|
|
if not self.__parameters[name].hasAttributeName(): self.__parameters[name].setAttributeName(name) |
|
|
if self.debug(): print "%s: parameter %s has now value %s"%(self,name,self.__parameters[name]) |
|
|
else: |
|
|
set=False |
|
|
for i in self.__submodels: |
|
|
try: |
|
|
self.__parameters[i].setParameter(**{name : parameters[name]}) |
|
|
set=True |
|
|
except IllegalParameterError: |
|
|
pass |
|
|
if not set: raise IllegalParameterError("%s: Attempt to set undeclared parameter %s."%(self,name)) |
|
|
|
|
|
def hasParameter(self,name): |
|
|
"""returns True if self or one of the submodels has parameter name""" |
|
|
if self.__parameters.has_key(name): |
|
|
out=True |
|
|
else: |
|
|
out=False |
|
|
for i in self.__submodels: out= out or self.__parameters[i].hasParameter(name) |
|
|
return out |
|
|
|
|
|
def checkParameter(self,name): |
|
|
"""checks if self has the parameter name. Otherewise ParameterError is thrown.""" |
|
|
if not self.hasParameter(name): |
|
|
raise ParameterError("%s has no parameter %s."%(str(self),name)) |
|
|
|
|
|
def __getattr__(self,name): |
|
|
"""returns the value for attribute name. If name is in the Link list, the corresponding attribute is returned.""" |
|
|
if self.__dict__.has_key(name): |
|
|
return self.__dict__[name] |
|
|
elif self.__dict__.has_key("_Model__parameters") and self.__dict__.has_key("_Model__submodels"): |
|
|
return self.getParameter(name) |
|
|
else: |
|
|
raise AttributeError,"No attribute %s."%name |
|
|
|
|
|
def __setattr__(self,name,value): |
|
|
"""returns the value for attribute name.""" |
|
|
if self.__dict__.has_key("_Model__parameters") and self.__dict__.has_key("_Model__submodels"): |
|
|
if self.hasParameter(name): |
|
|
self.setParameter(**{ name : value }) |
|
|
else: |
|
|
self.__dict__[name]=value |
|
|
else: |
|
|
self.__dict__[name]=value |
|
|
|
|
|
def __delattr__(self,name): |
|
|
"""removes the attribute name.""" |
|
|
if self.__dict__.has_key(name): |
|
|
del self.__dict__[name] |
|
|
elif self.__dict__.has_key("_Model__parameters"): |
|
|
self.deleteParameter(name) |
|
|
else: |
|
|
raise AttributeError,"No attribute %s."%name |
|
|
|
|
|
# |
|
|
# submodel handeling: |
|
|
# |
|
|
def doInitializationOfSubmodels(self): |
|
|
"""initializes the time stepping for all submodels.""" |
|
|
for i in self.__submodels: self.getParameter(i).doInitialization() |
|
|
|
|
|
def getSafeTimeStepSizeFromSubmodels(self): |
|
|
"""returns a time step size which can savely be used by all submodels. To avoid a big increase in the step size, |
|
|
the new step size is restricted to the double of the precious step size.""" |
|
|
out=None |
|
|
for i in self.__submodels: |
|
|
dt=self.getParameter(i).getSafeTimeStepSize() |
|
|
if not dt==None: |
|
|
if out==None: |
|
|
out=dt |
|
|
else: |
|
|
out=min(out,dt) |
|
|
return out |
|
|
|
|
|
def doStepOfSubmodels(self,t): |
|
|
"""executes the time step for each submodel""" |
|
|
for i in self.__submodels: self.getParameter(i).doStep(t) |
|
|
|
|
|
def finalizeAllSubmodels(self): |
|
|
"""returns True if all submodels can be finalized""" |
|
|
out=True |
|
|
for i in self.__submodels: out = out and self.getParameter(i).finalize() |
|
|
return out |
|
|
|
|
|
def doFinalizationOfSubmodels(self): |
|
|
"""finalalizes the time stepping for each of the submodels.""" |
|
|
for i in self.__submodels: self.getParameter(i).doFinalization() |
|
|
|
|
|
def doIterationInitializationOfSubmodels(self,t): |
|
|
"""initializes the iteration for each of the submodels.""" |
|
|
for i in self.__submodels: self.getParameter(i).doIterationInitialization(t) |
|
|
|
|
|
def doIterationStepOfSubmodels(self): |
|
|
"""executes the iteration step at time step for each submodel""" |
|
|
for i in self.__submodels: self.getParameter(i).doIterationStep() |
|
|
|
|
|
def terminateAllSubmodels(self): |
|
|
"""returns True if all iterations for all submodels are terminated.""" |
|
|
out=True |
|
|
for i in self.__submodels: out = out and self.getParameter(i).terminate() |
|
|
return out |
|
|
|
|
|
def doIterationFinalizationOfSubmodels(self): |
|
|
"""finalalizes the iteration process for each of the submodels.""" |
|
|
for i in self.__submodels: self.getParameter(i).doIterationFinalization() |
|
|
|
|
|
def checkPointSubmodels(self): |
|
|
"""performs check pointing for each submodel""" |
|
|
for i in self.__submodels: self.getParameter(i).checkPoint() |
|
|
|
|
|
# |
|
|
# these methods control the time stepping |
|
|
# |
|
|
def doInitialization(self): |
|
|
"""initializes the time stepping""" |
|
|
self.doInitializationOfSubmodels() |
|
|
|
|
|
def getSafeTimeStepSize(self): |
|
|
"""returns a time step size which can savely be used""" |
|
|
return self.getSafeTimeStepSizeFromSubmodels() |
|
|
|
|
|
def doStep(self,t): |
|
|
"""executes the time step by first iterating over time step t and then step forward""" |
|
|
# run iteration on simulation until terminated: |
|
|
self.doIterationInitialization(t) |
|
|
while not self.terminate(): self.doIterationStep() |
|
|
self.doIterationFinalization() |
|
|
self.doStepOfSubmodels(t) |
|
|
|
|
|
def finalize(self): |
|
|
"""returns True if all submodels are to be finalized""" |
|
|
return self.finalizeAllSubmodels() |
|
|
|
|
|
def doFinalization(self): |
|
|
"""finalizes the time stepping.""" |
|
|
self.doFinalizationOfSubmodels() |
|
|
# |
|
|
# methods deal with iterations: |
|
|
# |
|
|
def doIterationInitialization(self,t): |
|
|
"""initializes the iteration on a time step""" |
|
|
self.__iter=0 |
|
|
if self.debug(): print "%s: iteration starts"%self |
|
|
self.doIterationInitializationOfSubmodels(t) |
|
|
|
|
|
def doIterationStep(self): |
|
|
"""executes the iteration step""" |
|
|
self.__iter+=1 |
|
|
if self.debug(): print "%s: iteration step %d"%(self,self.__iter) |
|
|
try: |
|
|
self.doIterationStepOfSubmodels() |
|
|
except IterationDivergenceError,e: |
|
|
raise IterationDivergenceError("divergence at time step %s in iteration step %s by reason: \n%s."%(self.__n,self.__iter,e.value)) |
|
|
|
|
|
def terminate(self): |
|
|
"""returns True if time steping is terminated""" |
|
|
return self.terminateAllSubmodels() |
|
|
|
|
|
def doIterationFinalization(self): |
|
|
"""finalalizes the iteration process.""" |
|
|
self.doIterationFinalizationOfSubmodels() |
|
|
if self.debug(): print "%s: iteration finalized after %s step"%(self,self.__iter) |
|
|
# |
|
|
# sum other method: |
|
|
# |
|
|
def checkPoint(self): |
|
|
"""performs check pointing for each submodel""" |
|
|
if not self.__check_pointing==None: |
|
|
if self.__n%self.__check_pointing==0: self.checkPointsSubmodels() |
|
|
|
|
|
def run(self): |
|
|
"""After check_pointing time steps the model will start to create checkpoint files for each of the submodels""" |
|
|
self.__tn=0. |
|
|
self.__n=0 |
|
|
self.__dt=None |
|
|
self.doInitialization() |
|
|
while not self.finalize(): |
|
|
self.__n+=1 |
|
|
self.__dt=self.getSafeTimeStepSize() |
|
|
if self.__dt==None: self.__dt=self.UNDEF_DT |
|
|
if self.debug(): print "%s: %d. time step %e (step size %e.)"%(self,self.__n,self.__tn+self.__dt,self.__dt) |
|
|
endoftimestep=False |
|
|
while not endoftimestep: |
|
|
endoftimestep=True |
|
|
try: |
|
|
self.doStep(self.__tn+self.__dt) |
|
|
except FailedTimeStepError: |
|
|
self.__dt=self.getSafeTimeStepSize() |
|
|
if self.__dt==None: self.__dt=self.UNDEF_DT |
|
|
endoftimestep=False |
|
|
if self.debug(): print "%s: time step is repeated with new step size %e."%(self,self.__dt) |
|
|
except IterationDivergenceError: |
|
|
self.__dt*=0.5 |
|
|
endoftimestep=False |
|
|
if self.debug(): print "%s: iteration failes. time step is repeated with new step size %e."%(self,self.__dt) |
|
|
self.checkPoint() |
|
|
self.__tn+=self.__dt |
|
|
self.doFinalization() |
|
900 |
|
|
901 |
class IterationDivergenceError(Exception): |
class IterationDivergenceError(Exception): |
902 |
"""excpetion which should be thrown if an iteration at a time step fails""" |
""" |
903 |
|
Exception which is thrown if there is no convergence of the iteration |
904 |
|
process at a time step. |
905 |
|
|
906 |
|
But there is a chance that a smaller step could help to reach convergence. |
907 |
|
""" |
908 |
pass |
pass |
909 |
|
|
910 |
class FailedTimeStepError(Exception): |
class FailedTimeStepError(Exception): |
911 |
"""excpetion which should be thrown if the time step fails because of a step size that have been choosen to be to large""" |
""" |
912 |
|
Exception which is thrown if the time step fails because of a step |
913 |
|
size that have been choosen to be too large. |
914 |
|
""" |
915 |
pass |
pass |
916 |
|
|
917 |
class IllegalParameterError(Exception): |
class SimulationBreakDownError(Exception): |
918 |
"""excpetion which is thrown if model has not the desired parameter""" |
""" |
919 |
|
Exception which is thrown if the simulation does not manage to |
920 |
|
progress in time. |
921 |
|
""" |
922 |
pass |
pass |
923 |
|
|
924 |
|
class NonPositiveStepSizeError(Exception): |
925 |
|
""" |
926 |
|
Exception which is thrown if the step size is not positive. |
927 |
|
""" |
928 |
|
pass |
929 |
|
|
930 |
if __name__=="__main__": |
# vim: expandtab shiftwidth=4: |
|
class Messenger(Model): |
|
|
def __init__(self): |
|
|
Model.__init__(self,parameters={"message" : "none" },name="messenger") |
|
|
|
|
|
def doInitialization(self): |
|
|
print "I start talking now!" |
|
|
|
|
|
def doStep(self,t): |
|
|
print "Message (time %e) : %s "%(t,self.message) |
|
|
|
|
|
def doFinalization(self): |
|
|
print "I have no more to say!" |
|
|
|
|
|
# explicit scheme |
|
|
class Ode1(Model): |
|
|
def __init__(self,**args): |
|
|
Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 1. , "message" : "none" },name="Ode1",debug=True) |
|
|
|
|
|
def doInitialization(self): |
|
|
self._tn=0 |
|
|
|
|
|
def doStep(self,t): |
|
|
self.u=self.u+(t-self._tn)*self.a*self.u**2 |
|
|
self._tn=t |
|
|
|
|
|
def doFinalization(self): |
|
|
self.message="current error = %e"%abs(self.u-1./(1./3.-self.a*self._tn)) |
|
|
print self.message |
|
|
|
|
|
def getSafeTimeStepSize(self): |
|
|
return self.dt |
|
|
|
|
|
def finalize(self): |
|
|
return self._tn>=self.tend |
|
|
# explicit scheme |
|
|
class Ode2(Model): |
|
|
|
|
|
def __init__(self,**args): |
|
|
Model.__init__(self,parameters={"tend" : 1., "dt" : 0.0001 ,"a" : 0.1 ,"u" : 10000. },name="Ode2",debug=True) |
|
|
self.declareParameter(tol=1.e-8,message="none") |
|
|
|
|
|
|
|
|
def doInitialization(self): |
|
|
self._tn=0 |
|
|
self._iter=0 |
|
|
|
|
|
def doIterationInitialization(self,t): |
|
|
self._iter=0 |
|
|
self._u_last=self.u |
|
|
self._dt=t-self._tn |
|
|
self._tn=t |
|
|
|
|
|
def doIterationStep(self): |
|
|
self._iter+=1 |
|
|
self._u_old=self.u |
|
|
self.u=(self._dt*self.a*self.u**2-self._u_last)/(2*self._dt*self.a*self.u-1.) |
|
|
|
|
|
def terminate(self): |
|
|
if self._iter<1: |
|
|
return False |
|
|
else: |
|
|
return abs(self._u_old-self.u)<self.tol*abs(self.u) |
|
|
|
|
|
def doIterationFinalization(self): |
|
|
self.message="current error = %e"%abs(self.u-1./(1-self.a*self._tn)) |
|
|
print self.message |
|
|
|
|
|
def getSafeTimeStepSize(self): |
|
|
return self.dt |
|
|
|
|
|
def finalize(self): |
|
|
return self._tn>=self.tend |
|
|
|
|
|
# a simple model with paramemter tend, dt, p1, p2, and p3 |
|
|
class Test1(Model): |
|
|
|
|
|
def __init__(self,**args): |
|
|
Model.__init__(self,{"tend" : 1., "dt" : 0.1 ,"p1" : 0 ,"p2" : 0 ,"p3" : 0 },"test","bla",None,True) |
|
|
self.setParameters(args) |
|
|
|
|
|
def doInitialization(self): |
|
|
self.__tn=0 |
|
|
self.__n=0 |
|
|
|
|
|
def doStep(self,t): |
|
|
self.p3=self.p1+t*self.p2 |
|
|
self.__tn=t |
|
|
print "test1 set the value out1 to ",self.p3 |
|
|
|
|
|
def doFinalization(self): |
|
|
pass |
|
|
|
|
|
def getSafeTimeStepSize(self): |
|
|
return self.dt |
|
|
|
|
|
def finalize(self): |
|
|
return self._tn>self.tend |
|
|
|
|
|
|
|
|
class Test2(Model): |
|
|
|
|
|
def __init__(self): |
|
|
Model.__init__(self,{"q1": None},"test2","",None,True) |
|
|
|
|
|
|
|
|
def doInitialization(self): |
|
|
print "the whole thing starts" |
|
|
|
|
|
def doStep(self,t): |
|
|
print "test2 things that out1 is now ",self.out1 |
|
|
|
|
|
def doFinalization(self): |
|
|
print "all done" |
|
|
|
|
|
def finalize(self): |
|
|
return True |
|
|
|
|
|
class Test12(Model): |
|
|
"""model build from two models in a transperent way""" |
|
|
def __init__(self): |
|
|
Model.__init__(self,{"sm1": None, a : 0, "sm2": None},"test2","",None,True) |
|
|
self.setExecutionOrder(["sm2","sm1"]) |
|
|
|
|
|
# test messenger |
|
|
m=Messenger() |
|
|
m.run() |
|
|
# ode1 |
|
|
o=Ode1() |
|
|
o.dt=0.001 |
|
|
o.u=3. |
|
|
o.run() |
|
|
# ode1 |
|
|
o=Ode2() |
|
|
o.dt=0.01 |
|
|
o.a=0.1 |
|
|
o.u=1. |
|
|
o.run() |
|
|
# and they are linked together: |
|
|
o=Ode2() |
|
|
m=Messenger() |
|
|
om=Model(submodels=[o,m],debug=True) |
|
|
om.dt=0.01 |
|
|
om.u=1. |
|
|
m.message=Link(o) |
|
|
om.run() |
|
|
print om.showParameters() |
|
|
1/0 |
|
|
|
|
|
t=Test1() |
|
|
t.tend=1. |
|
|
t.dt=0.25 |
|
|
t.in1=1. |
|
|
t.in2=3. |
|
|
t.run() |
|
|
# and a coupled problem: |
|
|
t2=Test2() |
|
|
t2.out1=Link(t) |
|
|
Model([t,t2],debug=True).run() |
|