Search code examples
pythonarraysfortranf2py

Compiling f90 function that returns array using f2py


I have a subroutine that calculates a large array and writes it to a file. I'm trying to transform that into a function that returns that array. However, I'm getting a very weird error which seems to be connected to the fact that I am returning an array. When I try to return a float (as a test) it works perfectly fine.

Here's the MWE, which I call from python with mwe('dir', 'postpfile', 150, 90.):

FUNCTION mwe(dir, postpfile, nz, z_scale)
IMPLICIT NONE

INTEGER         :: nz
REAL(KIND=8)    :: z_scale
CHARACTER(len=100)      :: postpfile
CHARACTER(len=100)         :: dir
REAL(kind=8)  :: mwe

print*,'dir ', dir
print*,'postpfile ', postpfile
print*,'nz ', nz
print*,'Lz ', z_scale

mwe = 4.5d0
END FUNCTION mwe

This works well and prints, as expected:

 dir dir                                                                                                 
 postpfile postpfile                                                                                           
 nz          150
 Lz    90.000000000000000     

However, if I define the function as an array:

FUNCTION mwe(dir, postpfile, nz, z_scale)
IMPLICIT NONE

INTEGER         :: nz
REAL(KIND=8)    :: z_scale
CHARACTER(len=100)      :: postpfile
CHARACTER(len=100)         :: dir
REAL(KIND=8),DIMENSION (2,23)   :: mwe

print*,'dir ', dir
print*,'postpfile ', postpfile
print*,'nz ', nz
print*,'Lz ', z_scale

mwe = 4.5d0
END FUNCTION mwe

Then it prints this:

 dir postpfile                                                                                           
 postpfile ��:����������k�� 2����V@(����H���;�!��v
 nz            0
Segmentation fault (core dumped)

I am running f2py version 2, NumPy 1.11.1 and Python 3.5.1.

EDIT

I'm compiling with f2py -c -m fmwe fmwe.f90, and calling the function with mwe('dir', 'postpfile', 150, 90.).


Solution

  • I think the problem is coming from somewhere from lack of the explicit interface. (not sure may be someone else can point out what is the problem more precisely.)

    Even though, I am not sure about my explanation, I have 2 working cases. Changing your function into a subroutine or putting your function inside a module (which generates the explicit interface by itself) solves the issue you mentioned.

    Below script still can be called like my_sub('dir', 'postpfile', 150, 90.) from python.

    subroutine my_sub(mwe, dir, postpfile, nz, z_scale)
    implicit none
    
    integer,intent(in)             :: nz
    real(KIND=8),intent(in)        :: z_scale
    chracter(len=100),intent(in)   :: postpfile
    character(len=100), intent(in) :: dir
    real(KIND=8), intent(out)      :: mwe(2,23)
    
    print*,'dir ', dir
    print*,'postpfile ', postpfile
    print*,'nz ', nz
    print*,'Lz ', z_scale
    
    mwe = 4.5d0
    end subroutine my_sub
    

    If you use the function within a module, you need to make the call from python a little differently; test('dir', 'postpfile', 150, 90.).

    module test
    
    contains 
    function mwe(dir, postpfile, nz, z_scale)
    implicit none
    
    integer            :: nz
    real(KIND=8)       :: z_scale
    chracter           :: postpfile
    character(len=100) :: dir
    real(KIND=8)       :: mwe(2,23)
    
    print*,'dir ', dir
    print*,'postpfile ', postpfile
    print*,'nz ', nz
    print*,'Lz ', z_scale
    
    mwe = 4.5d0
    end function mwe
    
    end module test
    

    I did not try, but it will probably work with proper Fortran interface including your function.(assuming existence of the explicit interface is the key)

    I hope that someone will complete/correct my answer.