From charlesreid1

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 (via /path/to/cantera/src/zeroD/ReactorBase.cpp and /path/to/cantera/include/cantera/zeroD/ReactorBase.h)
  • Extending Cantera's C API to include the new function (via /path/to/cantera/src/clib/ctreactor.{cpp,h})
  • Extending Cantera's Python-C interface to utilize the new function (via /path/to/cantera/src/python/{ctreactor_methods.cpp,methods.h})
  • Calling the new method from Cantera's Python classes (via /path/to/cantera/interfaces/python/Cantera/Reactor.py)


Add New C++ Method

C++ Code First

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,36 @@
     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 );
+        }
+    }
+    m_nwalls--;
+}
+
+void ReactorBase::removeWalls()
+{
+    m_lr.clear();
+    m_wall.clear();
+    m_nwalls = 0;
+}
+
 Wall& ReactorBase::wall(size_t n)
 {
     return *m_wall[n];

Header File Next

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);

Recompile

Once you've added these changes, recompile Cantera. If you're using my scons configure/build script from the Cantera2 page, you'll just follow the instructions - delete cantera.conf, and re-run the scons configure/build script.


Extend Cantera's C API to Include New Method

C Code First

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()

Then The Header File

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

Modify C Glue

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.

Modify Header Glue

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:

===================================================================
--- interfaces/python/Cantera/Reactor.py	(revision 2)
+++ interfaces/python/Cantera/Reactor.py	(working copy)
@@ -336,6 +336,11 @@
         else:
             return self._paramid[n]

+    def removeWalls(self):
+        _cantera.reactor_removeWalls(self.reactor_id())
+        self._walls = []
+
+
 _reactorcount = 0
 _reservoircount = 0

We can test the new functionality with a simple test script in Python:

# First, initialize a reactor with surface kinetics
z = importPhase('GasSurfaceMech.xml','gas')
surf = importInterface('GasSurfaceMech.xml','catSurf',[z])
env=Reservoir(Air())
r=Reactor(z)

# Add a single wall and remove it
w1 = Wall(left=env,right=r,A=1.0,kinetics=[None,surf])
r.removeWalls()

# Add multiple walls and remove all of them
w2 = Wall(left=env,right=r,A=2.0,kinetics=[None,surf])
w3 = Wall(left=env,right=r,A=3.0,kinetics=[None,surf])
w4 = Wall(left=env,right=r,A=4.0,kinetics=[None,surf])
r.removeWalls()

# This should be 0
print len(r._walls)


Flags