简体   繁体   中英

f2py use arrays in Python callback function in Fortran

I'm using Fortran to supplement Python, and in a few methods I'm using a Python method as a callback in a Fortran subroutine. Everything seems to work, until I feed an array into the callback function as shown below.

RECURSIVE SUBROUTINE RECURSIVE_CURVE_SUBDIVISION(CPW, N, TOL, FUNC1)
    IMPLICIT NONE

    !F2PY INTENT(IN) CPW, N, TOL
    !F2PY DEPEND(N) CPW
    !F2PY (CALLBACK) FUNC1

    INTEGER, INTENT(IN) :: N
    DOUBLE PRECISION, INTENT(IN) :: CPW(0:N, 0:3), TOL

    INTEGER :: I
    DOUBLE PRECISION :: QP(0:N, 0:2), LP, LC, TEMP, &
                        AW(0:N, 0:3), BW(0:N, 0:3), V(0:2)

    EXTERNAL :: FUNC1

    DO I = 0, N
        QP(I, :) = CPW(I, 0:2) / CPW(I, 3)
    END DO
    LP = 0.0D0
    DO I = 0, N - 1
        V = QP(I + 1, :) - QP(I, :)
        CALL NORM(V, TEMP)
        LP = LP + TEMP
    END DO
    V = QP(N, :) - QP(0, :)
    CALL NORM(V, LC)
    IF (ABS(LP - LC) .LE. TOL) THEN
        CALL FUNC1(CPW, QP, LC, LP) !<-- here is the problem
        ! CALL FUNC1(LC, LP) !<-- this works
        ! CALL FUNC1(CPW=CPW, QP=QP, LC=LC, LP=LP)
        ! Added bonus if anyone can figure out how to use keyword arguements in
        ! the callback. For cleanliness, I'm trying to use func1(**kwargs) in Python.
    ELSE
        CALL SPLIT_BEZIER_CURVE(CPW, N, 0.50D0, AW, BW)
        CALL RECURSIVE_CURVE_SUBDIVISION(AW, N, TOL / 2.0D0, FUNC1)
        CALL RECURSIVE_CURVE_SUBDIVISION(BW, N, TOL / 2.0D0, FUNC1)
    END IF
END SUBROUTINE RECURSIVE_CURVE_SUBDIVISION

Here is some output when trying to compile with f2py (using gfortran):

warning C4244: '=' : conversion from 'npy_intp' to 'npy_int', possible loss of data
warning C4244: '=' : conversion from 'npy_intp' to 'npy_int', possible loss of data
warning C4244: '+=' : conversion from 'Py_ssize_t' to 'int', possible loss of data
warning C4244: '+=' : conversion from 'Py_ssize_t' to 'int', possible loss of data
warning C4244: '=' : conversion from 'Py_ssize_t' to 'int', possible loss of data
warning C4244: '=' : conversion from 'Py_ssize_t' to 'int', possible loss of data
warning C4244: '=' : conversion from 'Py_ssize_t' to 'int', possible loss of data
error C2065: 'n' : undeclared identifier
error C2065: 'n' : undeclared identifier

The module compiles just fine with gfortran by itself. I'm thinking I don't have enough info in the !F2PY section, but haven't figured out what I'm missing yet.

Any tips are greatly appreciated.

UPDATE 1:

So I noticed that I can return a 1 xn array, but amxn returns bogus results. For example, I can do CALL FUNC1(V) and it returns the 1 x 3 array V and prints it to the screen (the call-back function FUNC1 just prints to screen for now to test). When I substitute CP for V, it gives the warnings shown above and won't compile, so it's something to do with the shape of the array?

I don't remember where I saw this, but if I modify the statement near the top of the subroutine to:

!F2PY INTENT(IN) N, CPW, TOL
!F2PY DEPEND(N) CPW
!F2PY (CALLBACK) FUNC1
!F2PY CALL FUNC1(CP)
EXTERNAL :: FUNC1

it will compile and run, but the output from the call-back (just printing the array to screen) is bogus. It is a single float with wildly varying magnitude each iteration. Some kind of segmentation fault?

You have to pass N when you call the function so that it knows how big the arrays are (you don't have to handle this argument in Python, but it needs it at the C level). So change the call to

CALL FUNC1(CPW, QP, LC, LP,N)

If you don't do that, but look at the .pyf signature file generated ( f2py -m thingy -h thingy.pyf thingy.f90 ) the relevant part (it's autogenerated signature) is

python module recursive_curve_subdivision__user__routines 
    interface recursive_curve_subdivision_user_interface 
        subroutine func1(cpw,qp) ! in :thingy:thingy.f90:recursive_curve_subdivision:unknown_interface
            double precision dimension(n + 1,4),intent(in),depend(n) :: cpw
            double precision dimension(n + 1,3) :: qp
        end subroutine func1
    end interface recursive_curve_subdivision_user_interface
end python module recursive_curve_subdivision__user__routines

(Note that I've tested this with a slightly cut-down function call with only two arguments for simplicity, so it doesn't match your code exactly). You'll notice that it depends on N for all the array sizes, but you never pass N . If you do add N as an argument it knows the sizes and the print from Python works fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM