diff --git a/include/cantera/base/ctml.h b/include/cantera/base/ctml.h index c8e18ac5b..738ae1c03 100644 --- a/include/cantera/base/ctml.h +++ b/include/cantera/base/ctml.h @@ -790,6 +790,11 @@ std::string getChildValue(const Cantera::XML_Node& parent, void get_CTML_Tree(Cantera::XML_Node* node, const std::string file, const int debug = 0); +//! Read an ctml file from a file and fill up an XML tree. +//! @param file Name of the file +//! @return Root of the tree +Cantera::XML_Node getCtmlTree(const std::string file); + //! Convert a cti file into a ctml file /*! * diff --git a/interfaces/cython/.gitignore b/interfaces/cython/.gitignore new file mode 100644 index 000000000..539bab2a9 --- /dev/null +++ b/interfaces/cython/.gitignore @@ -0,0 +1,3 @@ +cantera/*.cpp +cantera/*.c +setup.py diff --git a/interfaces/cython/cantera/__init__.py b/interfaces/cython/cantera/__init__.py new file mode 100644 index 000000000..7a493060d --- /dev/null +++ b/interfaces/cython/cantera/__init__.py @@ -0,0 +1 @@ +from .solution import * diff --git a/interfaces/cython/cantera/substance.pxd b/interfaces/cython/cantera/substance.pxd new file mode 100644 index 000000000..0270e9bf4 --- /dev/null +++ b/interfaces/cython/cantera/substance.pxd @@ -0,0 +1,43 @@ +from libcpp.vector cimport vector +from utils cimport * + +cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": + cdef cppclass CxxThermoPhase "Cantera::ThermoPhase": + CxxThermoPhase() + double pressure() except + + void setMoleFractions(double*) except + + void getMassFractions(double*) except + + int nSpecies() + XML_Node& xml() + + +cdef extern from "cantera/kinetics/Kinetics.h" namespace "Cantera": + cdef cppclass CxxKinetics "Cantera::Kinetics": + CxxKinetics() + int nReactions() + + +cdef extern from "cantera/transport/TransportBase.h" namespace "Cantera": + cdef cppclass CxxTransport "Cantera::Transport": + CxxTransport(CxxThermoPhase*) + double viscosity() except + + + +cdef extern from "cantera/thermo/ThermoFactory.h" namespace "Cantera": + cdef CxxThermoPhase* newPhase(string, string) except + + cdef CxxThermoPhase* newPhase(XML_Node&) except + + + +cdef extern from "cantera/kinetics/KineticsFactory.h" namespace "Cantera": + cdef CxxKinetics* newKineticsMgr(XML_Node&, vector[CxxThermoPhase*]) except + + + +cdef extern from "cantera/transport/TransportFactory.h" namespace "Cantera": + cdef CxxTransport* newDefaultTransportMgr(CxxThermoPhase*) except + + cdef CxxTransport* newTransportMgr(string, CxxThermoPhase*) except + + + +cdef class _SolutionBase: + cdef CxxThermoPhase* thermo + cdef CxxKinetics* kinetics + cdef CxxTransport* transport diff --git a/interfaces/cython/cantera/substance.pyx b/interfaces/cython/cantera/substance.pyx new file mode 100644 index 000000000..69baf0f4b --- /dev/null +++ b/interfaces/cython/cantera/substance.pyx @@ -0,0 +1,84 @@ +import numpy as np +cimport numpy as np + +from cython.operator cimport dereference as deref + +from utils cimport * + +cdef class _SolutionBase: + def __cinit__(self, infile, phaseid=''): + rootNode = getCtmlTree(stringify(infile)) + + # Get XML data + cdef XML_Node* phaseNode + if phaseid: + phaseNode = rootNode.findID(stringify(phaseid)) + else: + phaseNode = rootNode.findByName(stringify('phase')) + if phaseNode is NULL: + raise ValueError("Couldn't read phase node from XML file") + + # Thermo + if isinstance(self, ThermoPhase): + self.thermo = newPhase(deref(phaseNode)) + else: + self.thermo = NULL + + # Kinetics + cdef vector[CxxThermoPhase*] v + + if isinstance(self, Kinetics): + v.push_back(self.thermo) + self.kinetics = newKineticsMgr(deref(phaseNode), v) + else: + self.kinetics = NULL + + # Transport + if isinstance(self, Transport): + self.transport = newDefaultTransportMgr(self.thermo) + else: + self.transport = NULL + + def __dealloc__(self): + del self.thermo + del self.kinetics + del self.transport + + +cdef class ThermoPhase(_SolutionBase): + property nSpecies: + def __get__(self): + return self.thermo.nSpecies() + + property pressure: + def __get__(self): + return self.thermo.pressure() + + def setMoleFractions(self, X): + if len(X) != self.nSpecies: + raise ValueError("Mole fraction array has incorrect length") + cdef np.ndarray[np.double_t, ndim=1] X_c = np.ascontiguousarray(X, dtype=np.double) + self.thermo.setMoleFractions(&X_c[0]) + + property massFractions: + def __get__(self): + cdef np.ndarray[np.double_t, ndim=1] X_c = np.empty(self.nSpecies) + self.thermo.getMassFractions(&X_c[0]) + return X_c + + +cdef class Kinetics(_SolutionBase): + property nReactions: + def __get__(self): + return self.kinetics.nReactions() + + +cdef class Transport(_SolutionBase): + property viscosity: + def __get__(self): + return self.transport.viscosity() + + +class Solution(ThermoPhase, Kinetics, Transport): + def __init__(self, *args, **kwars): + pass diff --git a/interfaces/cython/cantera/utils.pxd b/interfaces/cython/cantera/utils.pxd new file mode 100644 index 000000000..841660ee4 --- /dev/null +++ b/interfaces/cython/cantera/utils.pxd @@ -0,0 +1,14 @@ +from libcpp.string cimport string + +cdef extern from "cantera/base/xml.h" namespace "Cantera": + cdef cppclass XML_Node: + XML_Node* findByName(string) + XML_Node* findID(string) + int nChildren() + + +cdef extern from "cantera/base/ctml.h" namespace "ctml": + XML_Node getCtmlTree(string) except + + + +cdef string stringify(x) diff --git a/interfaces/cython/cantera/utils.pyx b/interfaces/cython/cantera/utils.pyx new file mode 100644 index 000000000..a7a91c60e --- /dev/null +++ b/interfaces/cython/cantera/utils.pyx @@ -0,0 +1,7 @@ +from utils cimport * + +cdef string stringify(x): + """ Converts Python strings to std::string. """ + # This method works with both Python 2.x and 3.x. + tmp = bytes(x.encode()) + return string(tmp) diff --git a/interfaces/cython/setup.py.in b/interfaces/cython/setup.py.in new file mode 100644 index 000000000..394f271c4 --- /dev/null +++ b/interfaces/cython/setup.py.in @@ -0,0 +1,37 @@ +import os +from distutils.core import setup, Extension +from distutils.sysconfig import get_config_var +from Cython.Distutils import build_ext + +dataFiles = ['_cantera%s' % get_config_var('SO')] +if os.name == 'nt': + dataFiles.append('cantera_shared.dll') +else: + dataFiles.append('libcantera_shared.so') + +canteraExtensions = [] + +def addExtension(name): + canteraExtensions.append(Extension("cantera.%s" % name, + ["cantera/%s.pyx" % name], + include_dirs=[".", "../../include"], + language="c++", + libraries=['cantera_shared','sundials_cvodes','sundials_idas','sundials_nvecserial','lapack','blas'], + library_dirs=['../../build/lib'], + extra_link_args=['-Wl,-rpath=$ORIGIN'])) + +addExtension('solution') +addExtension('utils') + +setup(name="Cantera", + version="2.1a1", + description="The Cantera Python Interface", + long_description=""" + """, + author="Raymond Speth", + author_email="speth@mit.edu", + url="http://code.google.com/p/cantera", + packages = ["cantera"], + cmdclass = {'build_ext': build_ext}, + ext_modules = canteraExtensions, + package_data = {'cantera': dataFiles}) diff --git a/src/base/ct2ctml.cpp b/src/base/ct2ctml.cpp index 59c484e3f..0c02887f8 100644 --- a/src/base/ct2ctml.cpp +++ b/src/base/ct2ctml.cpp @@ -279,4 +279,12 @@ void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string file, const int rootPtr->build(fin); fin.close(); } + +Cantera::XML_Node getCtmlTree(const std::string file) +{ + Cantera::XML_Node root; + get_CTML_Tree(&root, file); + return root; +} + }