1
2 from __future__ import division
3
4 from allimports import *
5 from PyDSTool.Generator import ODEsystem as ODEsystem
6 from baseclasses import Generator, theGenSpecHelper
7 from PyDSTool.utils import *
8 from PyDSTool.common import *
9 from PyDSTool.integrator import integrator
10 import PyDSTool.fixedpickle as fpickle
11
12
13 from numpy import Inf, NaN, isfinite, sometrue, alltrue, isnan, zeros
14 import math, random
15 from copy import copy, deepcopy
16 import os, platform, shutil, sys, gc
17 import distutils
18 from distutils.core import setup, Extension
19 from distutils.sysconfig import get_python_inc
20 from time import clock, sleep
21
22
23 import PyDSTool
24 _pydstool_path = PyDSTool.__path__[0]
25
26
27
28
30 """Wrapper for code generator for ADMC++32 and Matlab.
31 Uses Matlab functional specifications only."""
32
34 """Use the nobuild key to postpone building of the library, e.g. in
35 order to provide additional build options to makeLibSource and
36 compileLib methods or to make changes to the C code by hand.
37 No build options can be specified otherwise."""
38
39
40 if 'nobuild' in kw:
41 nobuild = kw['nobuild']
42 del kw['nobuild']
43 else:
44 nobuild = False
45 ODEsystem.__init__(self, kw)
46 self._solver = None
47 assert self.funcspec.targetlang == 'matlab', \
48 ('Wrong target language for functional specification. '
49 'matlab needed for this class')
50 assert isinstance(self.funcspec, RHSfuncSpec), ('ADMC++ '
51 'requires RHSfuncSpec type to proceed')
52 assert not self.inputs, \
53 'ADMC++ does not support external inputs feature'
54 self._errorcodes = {}
55 self._paraminfo = {}
56
57 self.vftype = 'vfieldts'
58
59
60
61
62 algparams_def = {'evtols' : 0.0001, 'vftype' : 'vfieldts'}
63
64
65 for k, v in algparams_def.iteritems():
66 if k not in self.algparams:
67 self.algparams[k] = v
68
69
70
71 if len(self.algparams) != len(algparams_def):
72 raise ValueError("Invalid keys present in algparams argument: " \
73 + str(remain(self.algparams.keys(),algparams_def.keys())))
74
75 thisplatform = platform.system()
76
77 self._compilation_tempdir = os.path.join(os.getcwd(),
78 "admcpp_temp")
79 if not os.path.isdir(self._compilation_tempdir):
80 try:
81 assert not os.path.isfile(self._compilation_tempdir), \
82 "A file already exists with the same name"
83 os.mkdir(self._compilation_tempdir)
84 except:
85 print "Could not create compilation temp directory " + \
86 self._compilation_tempdir
87 raise
88
89
90 self._model_dir = "@"+self.name
91 self._target_dir = os.path.join(self._compilation_tempdir,self._model_dir)
92
93 if not os.path.isdir(self._target_dir):
94 try:
95 assert not os.path.isfile(self._target_dir), \
96 "A file already exists with the same name"
97 os.mkdir(self._target_dir)
98 except:
99 print "Could not creat target ADMC model directory " + \
100 self._target_dir
101 raise
102
103
104 """ An ADMC model has the following files:
105 vfield.m -- contains code for the RHS of the vector field
106 set.m -- a generic method that overload matlab's set method; only need to insert vfield name
107 get.m -- a generic method that overloads matlab's get method; only need to insert appropriate parent name
108 """
109
110
111 self._model_file = self.name+".m"
112 self._ic_file = self.name+"_ics.m"
113 self._param_file = self.name+"_params.m"
114 self._set_file = "set.m"
115 self._get_file = "get.m"
116 self._vfield_file = "vfield.m"
117 self._events_file = "events.m"
118
119 self._vf_filename_ext = "_"+self._model_file[:-2]
120
121 if not nobuild:
122 self.makeLibSource()
123 else:
124 print "Build the library using the makeLib method, or in "
125 print "stages using the makeLibSource and compileLib methods."
126
127
129
130 eventDir = []
131
132
133
134
135 self._eventNames = self.eventstruct.sortedEventNames()
136 for evname in self._eventNames:
137 ev = self.eventstruct.events[evname]
138 assert isinstance(ev, MatlabEvent), ("ADMC++ can only "
139 "accept matlab events")
140
141 for evname in self._eventNames:
142 ev = self.eventstruct.events[evname]
143
144
145
146
147
148
149
150
151
152
153
155
156 allfilestr = ""
157
158 evname = self._eventNames
159 evcount = len(evname)
160 if evcount < 1:
161 return allfilestr
162
163 topstr = "function [vf_, ev_] = events(vf_, t_, x_, p_, state_)"
164 commentstr = "\n% Events method for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
165 alldefines = self._prepareVfieldDefines()
166
167 allfilestr = topstr + commentstr + alldefines
168
169 evassign = "\tev_ = zeros(1," + str(evcount) + ");\n"
170 for x in range(evcount):
171 ev = self.eventstruct.events[evname[x]]
172 evassign += "\tev_(" + str(x+1) + ") = " + ev.name + ev._LLargstr + ";\n"
173
174 allfilestr += evassign + "\n\n"
175
176 for x in range(evcount):
177
178 ev = self.eventstruct.events[evname[x]]
179 evfullfn = ""
180 evsig = "function " + ev._LLreturnstr + ev.name + ev._LLargstr + "\n"
181 assert ev._LLfuncstr.index(';') > 1, ("End your event function with a ';'")
182
183 fbody = "%BEGIN func " + ev.name + "\n" + alldefines
184 fbody += ev._LLfuncstr
185
186 if self.funcspec.auxfns:
187 fbody_parsed = addArgToCalls(fbody, self.funcspec.auxfns.keys(), "p_")
188 else:
189 fbody_parsed = fbody
190
191 evbody = "\n % Function definition\n" + fbody_parsed + "\n\n\n"
192 allevs = evsig + evbody
193 allfilestr += allevs
194
195 return allfilestr
196
198 allfilestr = ""
199
200 topstr = "function a = set(ain, varargin)\n"
201 commentstr = "% Set method for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
202
203 bodystr = "if nargin == 1\n" \
204 + "\t % Show input fields\n" \
205 + "\t todisp = structsub(struct(ain), ain.protectedfields{:});\n" \
206 + "\t disp(todisp);\n\t return \n end\n\n" \
207 + "a = ain;\n\n" \
208 + "if nargout < 1\n" + "\t warning('SET method invoked without output.');\n" \
209 + "\t disp(' ');\n" + "\t return\n" + "end\n\n" \
210 + "if rem(nargin-1, 2)\n" + "\t error('Wrong number of input arguments.');\n" + "end\n\n" \
211 + "args = {varargin{:}};\n\n" \
212 + "% Process input arguments\n" \
213 + "while ~isempty(args)\n" + "\t nam = args{1};\n\t val = args{2};\n\t args = args(3:end);\n\n" \
214 + "\t if any( strcmp( nam, a.privatefields ) )\n" + "\t\t warning(['Field ' nam ' is private -- unchanged.']);\n\n" \
215 + "\t elseif isfield(struct(a), nam)\n\t\t eval(['a.' name '= val;']);\n\n"
216
217 parentfieldstr = "\t % Set parent field\n" + "\t else\n" \
218 + "\t\t a." + str(self.vftype) + " = set(a." + str(self.vftype) + ", nam, val);\n\n" \
219 + "\t end\n" + "end\n\n" + "varargout{1} = a;\n"
220
221 allfilestr = topstr + commentstr + bodystr + parentfieldstr
222
223 return allfilestr
224
226 allfilestr = ""
227
228 topstr = "function varargout = get(a, nam)\n"
229 commentstr = "% Get method for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
230
231 bodystr = "if nargin < 2\n" \
232 + "\t % Show structure info\n" \
233 + "\t disp(struct(a));\n\t return \n end\n\n" \
234 + "if nargin > 2\n" + "\t warning('GET method invoked with more than two input arguments.');\n" \
235 + "\t disp(' ');\n" + "end\n\n" \
236 + "% Check for fields in this class; return values\n" \
237 + "if any( strcmp( nam, fieldnames(a) ) )\n" + "\t v = getfield(struct(a), nam);\n\n" \
238
239 parentfieldstr = "% Try parent field\n" + "else\n" \
240 + "\t v = get(a." + str(self.vftype) + ", nam);\n\n" \
241 + "end\n\n" + "varargout{1} = v;\n"
242
243 allfilestr = topstr + commentstr + bodystr + parentfieldstr
244
245 return allfilestr
246
247
249 pardefines = ""
250 vardefines = ""
251
252 vnames = self._var_ixmap
253 pnames = self.funcspec.pars
254 pnames.sort()
255
256 for i in xrange(self.numpars):
257 p = pnames[i]
258 pardefines += "\t" + p + " = p_(" + str(i+1) + ");\n"
259
260 for i in xrange(self.dimension):
261 v = vnames[i]
262 vardefines += "\t" + v + " = x_(" + str(i+1) + ");\n"
263
264 alldefines = "\n% Parameter definitions\n\n" + pardefines \
265 + "\n% Variable definitions\n\n" + vardefines
266
267 allfilestr = alldefines
268
269 return allfilestr
270
271 - def _prepareVfieldContents(self, vfdefines):
272 allfilestr = ""
273
274 topstr = "function [vf_, y_] = vfield(vf_, t_, x_, p_)\n"
275 commentstr = "% Vector field definition for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
276
277 bodystr = vfdefines
278
279
280
281
282 allfilestr = topstr + commentstr + bodystr + self.funcspec.spec[0]
283
284 return allfilestr
285
287
288 allfilestr = ""
289
290 if self.funcspec.auxfns:
291 for finfo in self.funcspec.auxfns.values():
292 fbody = finfo[0]
293
294 fbody_parsed = addArgToCalls(fbody,
295 self.funcspec.auxfns.keys(),
296 "p_", notFirst=True)
297
298 allfilestr += "\n" + fbody_parsed + "\n\n"
299
300
301
302
303 return allfilestr
304
305
306
308 allfilestr = ""
309 topstr = "function a = " + self.name + "(varargin)\n"
310 commentstr = "% Vf object definition for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
311
312 if len(self._eventNames) > 0:
313 objectstr = "\t\t vf = vfieldts('dimx'," + str(self.dimension) + ", 'eventdim', " \
314 + str(len(self._eventNames)) + ");\n\n"
315 else:
316 objectstr = "\t\t vf = vfieldts('dimx'," + str(self.dimension) + ");\n\n"
317
318 bodystr = "nargs = nargin\n\n" + "switch nargs\n" \
319 + "\t case 0\n" + "\t\t a.publicfields = {};\n" \
320 + "\t\t a.protectedfields = {};\n" + "\t\t a.privatefields = {};\n\n" \
321 + objectstr \
322 + "\t\t a = class(a, '" + self.name + "', vf);\n\n" \
323 + "\t case 1\n" + "\t\t if (isa(varargin{1}, '" + self.name + "'))\n" \
324 + "\t\t\t a = varargin{1};\n" + "\t\t else\n" \
325 + "\t\t\t error('Wrong argument type');\n" + "\t\t end\n\n" \
326 + "\t otherwise\n" + "\t\t a = " + self.name + ";\n" \
327 + "\t\t a = set(a, varargin{:});\n" + "end\n"
328
329 allfilestr = topstr + commentstr + bodystr
330
331 return allfilestr
332
334 allfilestr = ""
335 topstr = "function ics_ = " + self.name +"_ics()\n"
336 commentstr = "% Initial conditions for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
337
338 bodystr = "ics_ = [ ...\n"
339 if self.initialconditions:
340 icnames = self.initialconditions.keys()
341 icnames.sort()
342
343 for i in range(len(icnames)-1):
344 if isnan(self.initialconditions[icnames[i]]):
345 val = str(0.0)
346 else:
347 val = str(self.initialconditions[icnames[i]])
348
349 bodystr += val + ", ... % " + icnames[i] + "\n"
350
351 if isnan(self.initialconditions[icnames[len(icnames)-1]]):
352 val = str(0.0)
353 else:
354 val = self.initialconditions[icnames[len(icnames)-1]]
355
356 bodystr += val + " % " + icnames[len(icnames)-1] + " ...\n"
357
358 bodystr += "];\n"
359
360 allfilestr = topstr + commentstr + bodystr
361
362 return allfilestr
363
365 allfilestr = ""
366 topstr = "function pars__ = " + self.name +"_params()\n"
367 commentstr = "% Parameters for model " + self.name + "\n% Generated by PyDSTool for ADMC++ target\n\n"
368
369 bodystr = "pars_ = [ ...\n"
370 if self.pars:
371 pnames = self.pars.keys()
372 pnames.sort()
373
374 for i in range(len(pnames)-1):
375 bodystr += str(self.pars[pnames[i]]) + ", ... % " + pnames[i] + "\n"
376
377 bodystr += str(self.pars[pnames[len(pnames)-1]]) + " % " + pnames[len(pnames)-1] + " ...\n"
378
379 bodystr += "];\n"
380
381 allfilestr = topstr + commentstr + bodystr
382
383 return allfilestr
384
385
387 allevs = ""
388
389 if self._eventNames == []:
390 numevs = 0
391 else:
392 numevs = len(self._eventNames)
393 for ev in self._eventNames:
394 ev = self.eventstruct.events[evname]
395 evfullfn = ""
396 assert isinstance(ev, MatlabEvent), ("ADMC can only accept matlab events")
397 evsig = "function y_ = " + ev.name + "(vf_, t_, x_, p_)\n"
398
399 assert ev._LLfuncstr.index(';') > 1, ("Event function code error: "
400 "Have you included a ';' character at the end of "
401 "your function?")
402 fbody = ev._LLfuncstr
403
404
405 if self.funcspec.auxfns:
406 fbody_parsed = addArgToCalls(fbody, self.funcspec.auxfns.keys(), "p_")
407 if 'initcond' in self.funcspec.auxfns:
408 fbody_parsed = wrapArgInCall(fbody_parsed, 'initcond', ' ')
409
410 else:
411 fbody_parsed = fbody
412
413 allevs += evsig + vfdefines + fbody_parsed + "\n\n"
414
415 return allevs
416
417
418
419
420
421
422 - def makeLib(self, libsources=[], libdirs=[], include=[]):
423 """makeLib calls makeLibSource and then the compileLib method.
424 To postpone compilation of the source to a DLL, call makelibsource()
425 separately."""
426 self.makeLibSource()
427
428
430 """makeLibSource generates the MATLAB source for the vector field specification.
431 It should be called only once per vector field."""
432
433
434
435 self._prepareEventSpecs()
436
437
438 allfilestr = self._prepareModelContents()
439 modelfile = os.path.join(self._target_dir, self._model_file)
440 try:
441 file = open(modelfile, 'w')
442 file.write(allfilestr)
443 file.close()
444 except IOError, e:
445 print "Error opening file "+self._model_file+" for writing"
446 raise IOError, e
447
448
449 if len(self._eventNames) > 0:
450 allfilestr = self._prepareEventsFileContents() + self._prepareAuxContents()
451 eventsfile = os.path.join(self._target_dir, self._events_file)
452 try:
453 file = open(eventsfile, 'w')
454 file.write(allfilestr)
455 file.close()
456 except IOError, e:
457 print "Error opening file "+self._events_file+" for writing"
458 raise IOError, e
459
460
461
462 allfilestr = self._prepareICContents()
463 icfile = os.path.join(self._target_dir, self._ic_file)
464 try:
465 file = open(icfile, 'w')
466 file.write(allfilestr)
467 file.close()
468 except IOError, e:
469 print "Error opening file "+self._ic_file+" for writing"
470 raise IOError, e
471
472
473 allfilestr = self._prepareParamContents()
474 paramfile = os.path.join(self._target_dir, self._param_file)
475 try:
476 file = open(paramfile, 'w')
477 file.write(allfilestr)
478 file.close()
479 except IOError, e:
480 print "Error opening file "+self._param_file+" for writing"
481 raise IOError, e
482
483
484 allfilestr = self._prepareGetFileContents()
485 getfile = os.path.join(self._target_dir, self._get_file)
486 try:
487 file = open(getfile, 'w')
488 file.write(allfilestr)
489 file.close()
490 except IOError, e:
491 print "Error opening file "+self._get_file+" for writing"
492 raise IOError, e
493
494
495 allfilestr = self._prepareSetFileContents()
496 setfile = os.path.join(self._target_dir, self._set_file)
497 try:
498 file = open(setfile, 'w')
499 file.write(allfilestr)
500 file.close()
501 except IOError, e:
502 print "Error opening file "+self._set_file+" for writing"
503 raise IOError, e
504
505
506
507
508 allfilestr = self.funcspec.spec[0] + self._prepareAuxContents()
509 vffile = os.path.join(self._target_dir, self._vfield_file)
510 try:
511 file = open(vffile, 'w')
512 file.write(allfilestr)
513 file.close()
514 except IOError, e:
515 print "Error opening file "+self._vfield_file+" for writing"
516 raise IOError, e
517
518
519
520
521
522
525
526
527
528
529 symbolMapDict = {}
530
531
532 theGenSpecHelper.add(ADMC_ODEsystem, symbolMapDict, 'matlab')
533