Developing modules

Here follows some hints and suggestions in order to develop C modules to be integrated in the python development, and in particular, to be included in the NanoTCAD ViDES suite.

For a complete reference I suggest to study contents contained here
as well as the book of H.P. Langtangen
“Python scripting for computational science”
Developing C modules to be included in python is not difficult.
Let’s introduce a simple example, which introduces all the elements useful
for the 99% of the applications.
In particular, let’s say we want to develop a module to be called by python (e.g. test.c),
which as input receives a class with the following attributes:
  • an integer number N
  • a numpy array Phi

As output, the test function returns the attribute N increased by 10 (N=N+10) and Phi multiplied by 3 (Phi=3*Phi)

Let’s have a look at the C subroutine testing, already included in the NanoTCAD_ViDESmod.c.
First of all, we must include the head “Python.h” and “arrayobject.h”. Such headers must be the first to be defined.

#include “Python.h”
#include “arrayobject.h”
The header of the function is the following
static PyObject* py_testing(PyObject* self, PyObject* args)
Note that, according to python convention, the name of the function is preceded by py_
{
  int N,nx,i;
  double *Phi,*tempo;
  We need to declare the PyObject obj and temp_obj, which will be used in the following, 
    as well as Phi_array, which is the PyArrayObject
  PyObject *obj,*temp_obj;
  PyArrayObject *Phi_array;
  
  Let’s import the array in order to avoid segmentation fault
  import_array();
  The python object, which is a class, can be read as
  if (!PyArg_ParseTuple(args,”O”,&obj))
    {
      printf(“NOT A CLASS! \n”);
      exit(0);
    }
  The attribute N of the class is extracted though the command PyObject_GetAttrString
  temp_obj=PyObject_GetAttrString(obj,”N”);
  N=(int)PyInt_AsLong(temp_obj);
  printf(“N is equal to %d \n”,N);

  Once N has been read, it can be incremented by 10
  N=N+10;

Let’s now work on the numpy array Phi, extracting the attribute Phi of the class

  Phi_array=(PyArrayObject *)PyObject_GetAttrString(obj,”Phi”);

  Before going on, let’s spend few lines on the PyArrayObject.
    In particular, it represents the numpy array in C, whose main attributes are the following:
  • nd (int): the dimension of the numpy Array
  • int *dimensions: array of length nd; if nd=2 dimension[0] represent the number of rows, and dimension[1] the number of lines
  • char *data: pointer to the first data element of the numpy array.
  • int *strides: array of length nd, describing th enumber of bytes between two successive data elements for a fixed index.
  Accordingly to what said above, we read the dimension of the Phi vector as
  nx=Phi_array->dimensions[0];
  Phi=dvector(0,nx-1);
  printf(“Length og the array = %d \n”,nx);
  and the data of the vector can be read, storing everything on the C array Phi. Remember that we want to multiply each element of the Phi array by 3, so

  for (i=0;i<nx;i++)
    {
      tempo=(double *)(Phi_array->data+i*Phi_array->strides[0]);
      Phi[i]=(*tempo)*3;
    }
  
     Now we can return the modified elements N and Phi
  temp_obj=  PyArray_FromDimsAndData(1,&nx,PyArray_DOUBLE,(char*)Phi);
  PyObject_SetAttrString(obj,”Phi”,temp_obj);

  temp_obj=PyInt_FromLong(N);
  PyObject_SetAttrString(obj,”N”,temp_obj);

  The last two lines are important in order to avoid segmentation fault.
  char *s = “Hello from testing!”;
  return Py_BuildValue(“s”, s);
}

Such function is already included in the NanoTCAD_ViDESmod.c file.
In the same way, we could have edited a file “testing.c”, and included it in the NanoTCAD_ViDESmod.c
typing
#include “testing.c”
Obviously, one should had also included testing.o in the list of object file in the Makefile (and in Makefile.form).

Once done, we have to define the methods to be called by a python script.
In particular, it is sufficient to modify the PyMethodDef function, adding the highlighted line

static PyMethodDef ViDES_nanolabmod_methods[] = {
  {“nonuniformgridmod”, py_nonuniformgridmod, METH_VARARGS},
  {“testing”, py_testing, METH_VARARGS},
  {“CNT_charge_T”, py_CNT_charge_T, METH_VARARGS},
  {“CNTmode_charge_T”, py_CNTmode_charge_T, METH_VARARGS},
  {“GNR_charge_T”, py_GNR_charge_T, METH_VARARGS},
  {“CNT_atoms_coordinates”, py_CNT_atoms_coordinates, METH_VARARGS},
  {“solvePoisson”, py_solvePoisson, METH_VARARGS},
  {“GNR_atoms_coordinates”, py_GNR_atoms_coordinates, METH_VARARGS},
  {“GNRgap”, py_GNRgap, METH_VARARGS},
  {NULL, NULL}
};
To compile just type make clean and then make. 
In order to make changes effective, make clean has first to be typed, since the module is composed by static functions.
A simple python script calling the testing function could be the following

from ViDES_nanolab import *
class B:
    N=3;
    Phi=ones(10);

a=B;
testing(a);
That’s all folks!

Comments are closed.