def _import_python(self): """Import Python module with right-hand-side for this model.""" py_file = os.path.join(self.packagedir, "py.py") try: self.model = import_module(".py", self.package) except ImportError: with write_if_not_exists(os.path.join(self.packagedir, "__init__.py")) as f: pass # just create empty __init__.py to make a package with write_if_not_exists(os.path.join(self.packagedir, self.url.rsplit("/", 1)[-1])) as f: f.write(urlcache(self.url)) with write_if_not_exists(py_file) as f: if self.localfile: f.write(urlcache( "http://bebiservice.umb.no/bottle/cellml2py", data=urllib.urlencode(dict(cellml=self.cellml)))) else: f.write( urlcache("http://bebiservice.umb.no/bottle/cellml2py/" + self.url)) self.model = import_module(".py", self.package) try: with open(py_file, "rU") as f: self.py_code = f.read() except IOError: self.py_code = "Source file open failed"
def cythonize(self): """ Return Cython code for this model (further hand-tweaking may be needed). This just imports and calls :func:`cgp.physmod.cythonize.cythonize_model`. """ modulename_cython = self.package + ".cy" modelname = self.hash modelfilename = os.path.join(self.packagedir, "cy.pyx") try: __import__(modulename_cython) return sys.modules[modulename_cython] except ImportError: pyx, setup = cythonize_model(self.py_code, modelname) pyx = urlcache("http://bebiservice.umb.no/bottle/cellml2cy", data=urllib.urlencode(dict(cellml=self.cellml))) pyxname = modelfilename.replace("%s.py" % modelname, "cython/%s/m.pyx" % modelname) dirname, _ = os.path.split(pyxname) setupname = os.path.join(dirname, "setup.py") # make the cython and model directories "packages" cyinitname = os.path.join(dirname, os.pardir, "__init__.py") modelinitname = os.path.join(dirname, "__init__.py") with write_if_not_exists(cyinitname): pass # just create an empty __init__.py file with write_if_not_exists(modelinitname): pass # just create an empty __init__.py file with open(pyxname, "w") as f: f.write(pyx) with open(setupname, "w") as f: f.write(setup) cmd = "python setup.py build_ext --inplace" status, output = getstatusoutput(cmd, cwd=dirname) # Apparently, errors fail to cause status != 0. # However, output does include any error messages. if "cannot find -lsundials_cvode" in output: raise OSError("Cython-compilation of ODE right-hand side " "failed because SUNDIALS was not found.\n" "Status code: %s\nCommand: %s\n" "Output (including errors):\n%s" % (status, cmd, output)) if status != 0: raise RuntimeError("'%s'\nreturned status %s:\n%s" % (cmd, status, output)) try: __import__(modulename_cython) return sys.modules[modulename_cython] except StandardError, exc: raise ImportError("Exception raised: %s: %s\n\n" "Cython compilation may have failed. " "The compilation command was:\n%s\n\n" "The output of the compilation command was:\n%s" % (exc.__class__.__name__, exc, cmd, output))
def __init__(self, # pylint: disable=W0102,E1002,R url=None, workspace=None, exposure=None, changeset=None, variant=None, localfile=None, t=[0, 1], y=None, p=None, rename={}, use_cython=True, purge=False, **kwargs): """ Wrap autogenerated CellML->Python for use with pysundials M = Cellmlmodel(workspace, exposure, variant) downloads and caches Python code autogenerated for the CellML model identified by the (workspace, exposure, variant) triple, and wraps it in a class with convenience attributes and functions for use with pysundials. Defaults are the latest *exposure* and the first *variant* listed at the cellml.org site. If the non-wrapped Python code is in a local file, e.g. exported from OpenCell, http://www.cellml.org/tools/opencell/ use the "file://" protocol. >>> newmodel = Cellmlmodel("/newmodel", "file:c:/temp/exported.py") ... # doctest: +SKIP Here, "newmodel" is whatever name you'd like for the wrapper module, and "exported.py" is whatever name you saved the exported code under. (Strictly speaking, the URL should be "file:///c:/temp/exported.py", but the simpler version is also accepted by urllib.urlopen().) The constructor arguments are as follows: TODO: Update this to use (workspace, exposure, variant) as identifiers. exposure_workspace: identifiers in the repository at cellml.org, e.g. "732c32162c845016250f234416415bfc7601f41c/vanderpol_vandermark_1928_version01" for http://models.cellml.org/exposure/2224a49c6b39087dad8682648658775d. If only the workspace is given, will try to obtain the latest workspace from the repository. urlpattern : URL to non-wrapped Python code for model, with %(workspace)s and %(exposure)s placeholders for e.g. 732c32162c845016250f234416415bfc7601f41c vanderpol_vandermark_1928_version01 t, y : as for Cvodeint p : optional parameter vector purge : (re-)download model even if the file is already present? rename : e.g. dict with possible keys "y", "p", "a", whose values are mappings for renaming variables. You should rarely need this, but it is useful to standardize names of parameters to be manipulated, see e.g. ap_cvode.Tentusscher.__init__(). use_cython: if True, wrap the model for Cython and compile. Cython files are placed in ``$HOME/_cgptoolbox/_cellml2py/cython/modulename/``, and _cellml2py.cython.modulename.modulename is used in place of _cellml2py.modulename. >>> Cellmlmodel().dtype Dotdict({'a': None, 'p': dtype([('epsilon', '<f8')]), 'y': dtype([('x', '<f8'), ('y', '<f8')])}) >>> Cellmlmodel(rename={"y": {"x": "V"}, "p": {"epsilon": "newname"}}).dtype Dotdict({'a': None, 'p': dtype([('newname', '<f8')]), 'y': dtype([('V', '<f8'), ('y', '<f8')])}) See class docstring: ?Cellmlmodel for details. """ if not any([url, workspace, exposure, changeset, variant, localfile]): url = ("http://models.cellml.org/workspace/" "vanderpol_vandermark_1928/@@rawfile/" "371151b156888430521cbf15a9cfa5e8d854cf37/" "vanderpol_vandermark_1928.cellml") self.workspace = workspace self.exposure = exposure self.changeset = changeset self.variant = variant self.localfile = localfile if localfile : self.url = guess_url(self) else: self.url = url or guess_url(self) self.cellml = urlcache(self.url) self.tree = etree.parse(StringIO(self.cellml), parser) try: self.name = etree.ETXPath("//{%s}model/@name" % cml)(self.tree)[0] except IndexError: self.name = etree.ETXPath("//{%s}model/@name" % cml.replace( "1.0", "1.1"))(self.tree)[0] self.hash = "_" + hashlib.sha1(self.cellml).hexdigest()[:6] self.package = "_cellml2py." + self.hash self.packagedir = os.path.join(cgp_tempdir, "_cellml2py", self.hash) if purge: # Won't work if this model has already been loaded # because files in self.packagedir will be in use. try: shutil.rmtree(self.packagedir) except OSError: pass _head, tail = os.path.split(self.url.strip("/")) with write_if_not_exists(os.path.join(self.packagedir, tail)) as f: f.write(self.cellml) if use_cython: self._import_cython() else: self._import_python() if y is None: y = self.model.y0 self.legend = legend(self.model) dtype = dtypes(self.legend) # Rename fields if requested for i in "a", "y", "p": if i in rename: L = eval(str(dtype[i])) for j, (nam, typ) in enumerate(L): if nam in rename[i]: L[j] = (rename[i][nam], typ) dtype[i] = np.dtype(L) # if there are no parameters or algebraic variables, make empty recarray try: pr = self.model.p.view(dtype.p, np.recarray) except TypeError: pr = np.array([]).view(np.recarray) try: self.algebraic = self.model.algebraic.view(dtype.a, np.recarray) except TypeError: self.algebraic = np.array([]).view(np.recarray) self.y0r = self.model.y0.view(dtype.y, np.recarray) super(Cellmlmodel, self).__init__(self.model.ode, t, y.view(dtype.y), pr, **kwargs) assert all(dtype[k] == self.dtype[k] for k in self.dtype) self.dtype.update(dtype) self.originals["y0r"] = self.y0r if p: self.model.p[:] = p
mem = joblib.Memory(os.path.join(gettempdir(), "cellmlmodel"), verbose=0) @mem.cache def urlcache(url, data=None): """Cache download from URL.""" with closing(urllib.urlopen(url, data)) as f: return f.read() # Ensure that $HOME/_cgptoolbox/_cellml2py/ is a valid package directory # This makes it easy to force re-generation of code by renaming _cellml2py/ try: import _cellml2py # @UnusedImport pylint: disable=W0611,W0403 except ImportError: initfile = os.path.join(cgp_tempdir, "_cellml2py", "__init__.py") with write_if_not_exists(initfile): pass # just create an empty __init__.py file # This must be done *after* creating cgp_tempdir, # otherwise it gets silently ignored, cf. # http://docs.python.org/2/using/cmdline.html#envvar-PYTHONPATH if cgp_tempdir not in sys.path: sys.path.append(cgp_tempdir) @mem.cache def generate_code(url_or_cellml, language="python"): """ Generate Python code for CellML model at url. Wraps cellml-api/testCeLEDS. Written as a replacement for the code generation at models.cellml.org, which was broken at the time of writing: