2

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 x n array, but a m x n 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?

5
  • Can you provide an explicit interface for func1? Commented Nov 30, 2015 at 21:48
  • Does this page possibly help...? folk.uio.no/hpl/scripting/doc/python/fc/… Commented Dec 1, 2015 at 16:39
  • This page might also be useful (though not f2py...) fortran90.org/src/best-practices.html#interfacing-with-python Commented Dec 1, 2015 at 17:13
  • Thanks @francescalus. I've been working at it with still no luck. I did post an update, although not sure if it means I'm any closer to solving the problem...seems strange to me. Commented Dec 9, 2015 at 20:25
  • Thanks @roygvib . I took a look but still didn't get me there. I did post an update, in case it gives you any more clues. Thanks! Commented Dec 9, 2015 at 20:26

1 Answer 1

2

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.

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

1 Comment

Thanks @DavidW. I also removed the !F2PY CALL FUNC1(CP) line at the top. I appreciate the help!

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.