MarcoMag
MarcoMag

Reputation: 651

Cannot pass multi-dimensional array to C function in *.pyx

I am desperately trying to write a wrapper to call a C function that:

In the code I attached, I put 2 functions that I am trying make accessible from python. The first function SurfInfo that calls the C function PosAndTgtSurfVec and this one works as expected. To make it work I used this tutorial. The problem lies in the second wrapped function Surf2Surfthat calls the C function SurfToSurf. Note that I can't modify the C functions.

The code is the following one:

import cython
cimport numpy as np
import numpy as np
from cython cimport view
from cpython cimport array

cdef extern void PosAndTgtSurfVec(double v[156],double X[18],double u[18]
     ,double t1[3],double t2[3],double (*a),double (*b),double (*xi),double (*theta)
     ,double s[3],double sxi[3],double stheta[3])

cdef extern void SurfToSurf(double v[5901],double X[2][12],double u[2][12]
     ,double t1[2][3],double t2[2][3],double a[2],double b[2],double hFG[4]
     ,double h[4],double R[24],double K[24][24])

# ---------------------------------------------------------#
# This one works ! 
def SurfInfo(
        np.ndarray[np.double_t, ndim=1, mode="c"] X,
        np.ndarray[np.double_t, ndim=1, mode="c"] u,
        np.ndarray[np.double_t, ndim=1, mode="c"] t1,
        np.ndarray[np.double_t, ndim=1, mode="c"] t2,
        double a,
        double b,
        double xi,
        double theta
       ):

    # declarations of pure C variables
    # used for output
    cdef :
        double v[156];
        double s[3];
        double sxi[3];
        double stheta[3];

    PosAndTgtSurfVec(v, &X[0], &u[0], &t1[0], &t2[0], &a, &b, &xi, &theta, s, sxi, stheta) 
    return s, sxi, stheta
# ---------------------------------------------------------#


# ---------------------------------------------------------#
# This one does not compile! 
def Surf2Surf(
    np.ndarray[double, ndim=2, mode="c"] X not None,
    np.ndarray[double, ndim=2, mode="c"] u,
    np.ndarray[double, ndim=2, mode="c"] t1,
    np.ndarray[double, ndim=2, mode="c"] t2,
    np.ndarray[double, ndim=1, mode="c"] a,
    np.ndarray[double, ndim=1, mode="c"] b,
    np.ndarray[double, ndim=1, mode="c"] hFG):

    cdef double v[5901]; 
    cdef double h[4]; 
    cdef double R[24]; 
    cdef double K[24][24];

    SurfToSurf(v, &X[0,0], &u[0,0], &t1[0,0], &t2[0,0], &a[0], &b[0], &hFG[0],h, R,K)
    return (h, R, K)
# ---------------------------------------------------------#

The compilation error is:

SurfToSurf(v, &X[0,0], &u[0,0], &t1[0,0], &t2[0,0], &a[0], &b[0], &hFG[0],h, R,K)
                 ^
------------------------------------------------------------

wrp.pyx:54:18: Cannot assign type 'double *' to 'double (*)[12]'

How do I provide the required type ? I thought that providing the address of the first element of the array as I did in the first case would work. I am sorry if the question is stupid and I would be glad if you could redirect me towards documentation or example that could help me solve the problem (I already checked this link and this book but I surely missed the answer in there) I am not looking for performances for the time being.

Upvotes: 3

Views: 467

Answers (1)

DavidW
DavidW

Reputation: 30915

Second try....

2D C arrays like double[2][12] are apparently stored as an array of double[12]s (which is convertible to a pointer to a double[12]). Therefore they are actually contiguous in memory and so should be compatible with a C-contiguous numpy array (like the ones you pass in).

All you have to do is a cast to reinterpret the double* that is the first element of the numpy array to a pointer to a double[12]r. (Casts are never ideal because they can often hide logic errors but I don't think there's another option in this case):

# I'm using this instead of `cdef extern` just to provide an easy way
# of printing the elements and thus confirming that it works
cdef void SurfToSurf(double[2][12] x):
    for j in range(2):
        for i in range(12):

def Surf2Surf(np.ndarray[double, ndim=2, mode="c"] X):
    assert X.shape[0]>=2 and X.shape[1]==12

    SurfToSurf(<double (*)[12]>&X[0,0])
            print(x[j][i])

I've added an assert in to check the size of your arrays.Without it you risk having your program crash if a too-small array is ever passed through the Python interface.

Upvotes: 4

Related Questions