3

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

  • takes several multi-dimensional C-contiguous array as input
  • use the arrays in a C routine to perform some operations

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.

3
  • 1
    While a pointer can be treated as a one-dimensional-array, the same is not true with two-dimensional arrays. It does not know how many elements are in a row to know how much it needs to offset to index each row. edit: I see you are trying to pass a pointer in as a 2d array rather than the other way around which is where people usually trip up. Commented Nov 3, 2017 at 20:02
  • @MacroMag I deleted my answer - it would have worked for a function defined to take double** but it turns out I didn't understand 2D C arrays as well as I thought, so it isn't right for those. Sorry for being misleading. I don't know the right answer I'm afraid. Commented Nov 4, 2017 at 10:26
  • @MarcoMag See edit - this version does work... Commented Nov 4, 2017 at 11:56

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.