4

Recently I wanted to call Python from Fortran (see here) using a C wrapper. Below I post a simpler example. Second call of the function results in Segmentation fault at pModule = PyImport_Import(pName). I figured out that the problem is with the from scipy.optimize import newton line - if I comment it everything works fine. Any ideas how to fix it?

rootC.c

#include "rootC.h"
#include <Python.h>

void root_(double* A, double* B, double* t, double* x)
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue, *sys, *path;

    Py_Initialize();

    sys  = PyImport_ImportModule("sys");
    path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString("."));

    pName = PyString_FromString("rootPY");
    pModule = PyImport_Import(pName);

    if (!pModule)
    {
        PyErr_Print();
        printf("ERROR in pModule\n");
        exit(1);
    }

    pFunc = PyObject_GetAttrString(pModule, "root");
    pArgs = PyTuple_New(3);
    PyTuple_SetItem(pArgs, 0, PyFloat_FromDouble((*A)));
    PyTuple_SetItem(pArgs, 1, PyFloat_FromDouble((*B)));
    PyTuple_SetItem(pArgs, 2, PyFloat_FromDouble((*t)));

    pValue = PyObject_CallObject(pFunc, pArgs);
    *x     = PyFloat_AsDouble(pValue);

    Py_Finalize();
}

rootC.h

#ifndef ROOT_H_
#define ROOT_H_

void root_(double*, double*, double*, double*);

#endif

rootPY.py

from mpmath import polylog, exp
from scipy.optimize import newton

def root(A,B,t):   
    return 1

main.c

#include "rootC.h"
#include <stdio.h>

int main() 
{
    double A = 0.4, B = 0.3, t = 0.1, x = 0.0;

    root_(&A,&B,&t,&x);
    printf("x = %.15f\n", x);

    root_(&A,&B,&t,&x);
    printf("x = %.15f\n", x);

    return 0;
}

Makefile

CC = gcc
FC = gfortran
CFLAGS = -I/usr/include/python2.7
LFLAGS = -L/usr/local/lib -lpython2.7 -lm

.PHONY: all clean

all: main

main: main.o rootC.o
    $(CC) $^ -o $@ $(LFLAGS)

main.o: main.c
    $(CC) $(CFLAGS) -c $< -o $@

rootC.o: rootC.c
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -f *.o
Community
  • 1
  • 1
Bociek
  • 1,195
  • 2
  • 13
  • 28
  • Why are you calling `PyImport_Import` instead of `PyImport_Module` as you do for the other modules? Also, I would check functions returning pointers for `NULL` unless the documentation explicitly says that it cannot be `NULL`. – Iharob Al Asimi Dec 19 '15 at 13:33
  • I changed line PyImport_ImportModule("rootPy"), but still the same problem and additionally it hangs a little during the second call before I get the result. – Bociek Dec 19 '15 at 13:47
  • I see that you don't have warnings enabled in your flags, why? They can help you spot stupid mistakes (that everyone makes). Add `-Wall -Wextra -Werror` to your `CFLAGS` and see if it helps. – Iharob Al Asimi Dec 19 '15 at 13:57
  • As noted [here](http://stackoverflow.com/q/7676314/5509239) and [here](http://stackoverflow.com/q/14843408/5509239) `numpy` doesn't like to be initialized twice. – memoselyk Dec 19 '15 at 17:39

2 Answers2

3

One can also do something like this:

#include "rootC.h"
#include <Python.h>
#include <stdlib.h>

void  Initialize ()
{ 
    Py_Initialize();
}

void Finalize ()
{
    Py_Finalize();
}

void root_(double* A, double* B, double* t, double* x)
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue, *sys, *path;
    static int i;

    if (i == 0)
    {   
        ++i;
        Initialize();
        atexit(Finalize);
    }

    ....
}
Bociek
  • 1,195
  • 2
  • 13
  • 28
2

The issue is with initializing and finalizing your python shell every time the function is called. (it is trying to initialise a .dll more than once)

Edit: Link to resource

This is my fix... ( not the best code for the job) in your main.c

int main() 
{
    double A = 0.4, B = 0.3, t = 0.1, x = 0.0;
    bool Stop = false;
    root_(&A,&B,&t,&x,&Stop);
    printf("x = %.15f\n", x);

    Stop=true;
    root_(&A,&B,&t,&x,&Stop);
    printf("x = %.15f\n", x);



    return 0;
}

then in your rootC.h

void root_(double*, double*, double*, double*,bool*);

then in rootC.c

#include "rootC.h"
#include <Python.h>

void root_(double* A, double* B, double* t, double* x,bool* Stop)
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue, *sys, *path;
    if (*Stop==false)
    {
        Py_Initialize();
    }
    sys  = PyImport_ImportModule("sys");
    path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString("."));

    pName = PyString_FromString("rootPY");
    pModule = PyImport_Import(pName);

    if (!pModule)
    {
        PyErr_Print();
        printf("ERROR in pModule\n");
        exit(1);
    }

    pFunc = PyObject_GetAttrString(pModule, "root");
    pArgs = PyTuple_New(3);
    PyTuple_SetItem(pArgs, 0, PyFloat_FromDouble((*A)));
    PyTuple_SetItem(pArgs, 1, PyFloat_FromDouble((*B)));
    PyTuple_SetItem(pArgs, 2, PyFloat_FromDouble((*t)));

    pValue = PyObject_CallObject(pFunc, pArgs);
    *x     = PyFloat_AsDouble(pValue);
    if (*Stop==true)
    {
        Py_Finalize();
    }
}

This should work :)

Community
  • 1
  • 1
Pomadomaphin
  • 116
  • 6
  • This [answer](http://stackoverflow.com/a/7676916/5509239) show an alternative to the `Stop` variable. And do you really need to pass `Stop` by-reference? – memoselyk Dec 19 '15 at 17:39
  • 1
    i think the above is just what happens when i try to write code after 3am – Pomadomaphin Dec 19 '15 at 23:20