Cantera/Extending C API: Difference between revisions
From charlesreid1
| Line 188: | Line 188: | ||
def __init__(self, contents = None, name = '', | def __init__(self, contents = None, name = '', | ||
volume = 1.0, energy = 'on', | volume = 1.0, energy = 'on', | ||
</pre> | |||
We can test the new functionality with a simple test script: | |||
<pre> | |||
surf = importInterface('Curran3B_17-Jun.xml','catSurf',[z]) | |||
z = importPhase('Curran3B_17-Jun.xml','gas') | |||
surf = importInterface('Curran3B_17-Jun.xml','catSurf',[z]) | |||
env=Reservoir(Air()) | |||
r=Reactor(z) | |||
w = Wall(left=env,right=r,A=1.0,kinetics=[None,surf]) | |||
</pre> | </pre> | ||
Revision as of 22:52, 27 August 2013
This article discusses extending Cantera's C API. This allows you to create a new class or function in Cantera, and create a handle for that object or function from Cantera's Python API. (Note: this guide covers the API for Cantera 2.0.)
To illustrate this procedure, I'll be adding a method to Cantera's ReactorBase class to remove all of the walls in a reactor. This article covers the following steps:
- Adding a C++ function to the ReactorBase object
- Extending Cantera's C API to include the new function
- Extending Cantera's Python-C interface to utilize the new function
- Calling the method from Python
Add New C++ Method
I'll add the following two methods to ReactorBase.cpp, to allow me to either remove a specific wall, or remove all walls:
===================================================================
--- ReactorBase.cpp (revision 2)
+++ ReactorBase.cpp (working copy)
@@ -72,6 +72,30 @@
m_nwalls++;
}
+void ReactorBase::removeWall(Wall& w)
+{
+ vector_int::iterator iL = m_lr.begin();
+ for( std::vector<Wall*>::iterator iW = m_wall.begin();
+ iW != m_wall.end();
+ ++iW, ++iL ) {
+ if ( (*iW) == &w) {
+ m_wall.erase( iW );
+ m_lr.erase( iL );
+ }
+ }
+}
+
+void ReactorBase::removeWalls()
+{
+ vector_int::iterator iL = m_lr.begin();
+ for( std::vector<Wall*>::iterator iW = m_wall.begin();
+ iW != m_wall.end();
+ ++iW, ++iL ) {
+ m_wall.erase(iW);
+ m_lr.erase(iL);
+ }
+}
+
Wall& ReactorBase::wall(size_t n)
{
return *m_wall[n];
and modify the ReactorBase header file as follows:
===================================================================
--- include/cantera/zeroD/ReactorBase.h (revision 2)
+++ include/cantera/zeroD/ReactorBase.h (working copy)
@@ -91,6 +91,11 @@
}
void addWall(Wall& w, int lr);
+
+ void removeWall(Wall& w);
+
+ void removeWalls();
+
Wall& wall(size_t n);
Once you've added these changes, compile Cantera (see the Cantera2 page for my scons configure/build script.)
Extend Cantera's C API to Include New Method
Cantera uses a C-language API (application programming interface) to provide a way for non-C++ programs (Fortran, Python, Matlab, etc.) to interface with Cantera. This interface is contained in /path/to/cantera/src/clib.
In the main code file (ctreactor.cpp), there are sections for reactor networks, reactors, walls, etc. Find the section for reactor methods, and add the following method:
===================================================================
--- ctreactor.cpp (revision 2)
+++ ctreactor.cpp (working copy)
@@ -282,7 +282,18 @@
}
}
+ int reactor_removeWalls( int i )
+ {
+ try {
+ ReactorBase* r = &ReactorCabinet::item(i);
+ ((Reactor*)r)->removeWalls(); //((Reactor*)r)->addSensitivityReaction(rxn);
+ return 0;
+ } catch (...) {
+ return handleAllExceptions(-1, ERR);
+ }
+ }
+
// reactor networks
int reactornet_new()
Next, add the corresponding entry to the header file:
===================================================================
--- ctreactor.h (revision 2)
+++ ctreactor.h (working copy)
@@ -28,6 +28,7 @@
CANTERA_CAPI double reactor_pressure(int i);
CANTERA_CAPI double reactor_massFraction(int i, int k);
CANTERA_CAPI size_t reactor_nSensParams(int i);
+ CANTERA_CAPI int reactor_removeWalls(int i);
CANTERA_CAPI int reactor_addSensitivityReaction(int i, int rxn);
CANTERA_CAPI int flowReactor_setMassFlowRate(int i, double mdot);
Now the Cantera C API has a way to call the ReactorBase::removeWalls() function. The last step necessary to call this method from Python is to create the glue between Python and the C API.
Extend Cantera's C-Python Interface to Include New Method
The C-Python interface glue is all contained in /path/to/cantera/src/python. The code utilizing the C API for reactors is in ctreactor_methods.cpp. A new Python method, called from C, is defined as follows:
===================================================================
--- ctreactor_methods.cpp (revision 2)
+++ ctreactor_methods.cpp (working copy)
@@ -127,6 +127,21 @@
}
+
+ static PyObject*
+py_reactor_removeWalls(PyObject* self, PyObject* args)
+{
+ int _val;
+ int i;
+ if (!PyArg_ParseTuple(args,"reactor_removeWalls", &i)) {
+ return NULL;
+ }
+ _val = reactor_removeWalls(i);
+ if (int(_val) == -1) {
+ return reportCanteraError();
+ }
+ return Py_BuildValue("i",_val);
+}
static PyObject*
py_flowReactor_setMassFlowRate(PyObject* self, PyObject* args)
{
This defines a Python method for calling the reactor removeWalls() function. As before, we'll need to create a corresponding entry in the header file:
===================================================================
--- methods.h (revision 2)
+++ methods.h (working copy)
@@ -240,6 +240,7 @@
{"reactor_massFraction", py_reactor_massFraction, METH_VARARGS},
{"reactor_nSensParams", py_reactor_nSensParams, METH_VARARGS},
{"reactor_addSensitivityReaction", py_reactor_addSensitivityReaction, METH_VARARGS},
+ {"reactor_removeWalls", py_reactor_removeWalls, METH_VARARGS},
{"flowReactor_setMassFlowRate", py_flowReactor_setMassFlowRate, METH_VARARGS},
{"reactornet_rtol", py_reactornet_rtol, METH_VARARGS},
{"reactornet_atol", py_reactornet_atol, METH_VARARGS},
The logical chain is now as follows:
- Python method py_reactor_removeWalls() calls C (Cantera API) method reactor_removeWalls()
- C (Cantera API) method reactor_removeWalls() calls C++ method ReactorBase::removeWalls()
Call New Method from Python Classes
Now that we've created a Python handle for the C API of the C++ code, we have to call the Python handle from the Python code.
We'll edit Cantera's Python class for Reactors and add a new removeWalls() method. Start by finding Cantera's Python classes, in /path/to/cantera/interfaces/python/Cantera, and add a new method to the Reactor class:
===================================================================
--- Reactor.py (revision 2)
+++ Reactor.py (working copy)
@@ -434,7 +434,10 @@
def setMassFlowRate(self, mdot):
_cantera.flowReactor_setMassFlowRate(self.reactor_id(), mdot)
+ def removeWalls(self):
+ _cantera.reactors_removeWalls(self.reactor_id())
+
class ConstPressureReactor(ReactorBase):
def __init__(self, contents = None, name = '',
volume = 1.0, energy = 'on',
We can test the new functionality with a simple test script:
surf = importInterface('Curran3B_17-Jun.xml','catSurf',[z])
z = importPhase('Curran3B_17-Jun.xml','gas')
surf = importInterface('Curran3B_17-Jun.xml','catSurf',[z])
env=Reservoir(Air())
r=Reactor(z)
w = Wall(left=env,right=r,A=1.0,kinetics=[None,surf])