numpyfortrangfortranf2py

f2py gives error when subroutine contains internal procedures (but compiles successfully with gfortran)


Following program compiles(in f2py) when the subroutine doesn't contain any internal procedures but fails to compile in f2py (compiles with gfortran) when it contains any internal procedure

Fortran code:

subroutine example(array)
    implicit none
    real*8::array(:,:)
    INTEGER::i,j
!f2py intent(inout)::array

    do i=1,3
        do j=1,3
            array(i,j)=10*i+j
        enddo
        ! print*,''
    enddo
    call tentimes(array)
    RETURN

    contains

    subroutine tentimes(array)
        implicit none
!f2py intent(inout):array
        real*8::array(:,:)
        array=array*10
    end subroutine tentimes



end subroutine example



program dummy
    implicit none
    
end program dummy

compiled with python -m numpy.f2py -c allocate.f90 -m ftest

and the python code :

import numpy as np

a=np.zeros((3,3),order="F")

print(a)
res=ftest.example(a)
print(a)

Why is this happening?

Error generated by f2py:

compiling Fortran sources
Fortran f77 compiler: /usr/bin/gfortran -Wall -g -ffixed-form -fno-second-underscore -fPIC -O3 -funroll-loops
Fortran f90 compiler: /usr/bin/gfortran -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops
Fortran fix compiler: /usr/bin/gfortran -Wall -g -ffixed-form -fno-second-underscore -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops
compile options: '-I/tmp/tmpwd_qygau/src.linux-x86_64-3.9 -I/home/srj/.local/lib/python3.9/site-packages/numpy/core/include -I/usr/include/python3.9 -c'
gfortran:f90: allocate.f90
allocate.f90:17:31:

   17 |     subroutine tentimes(array_i)
      |                               1
Warning: Unused dummy argument ‘array_i’ at (1) [-Wunused-dummy-argument]
allocate.f90:17:23:

   17 |     subroutine tentimes(array_i)
      |                       ^
Warning: ‘tentimes’ defined but not used [-Wunused-function]
gfortran:f90: /tmp/tmpwd_qygau/src.linux-x86_64-3.9/ftest-f2pywrappers2.f90
/tmp/tmpwd_qygau/src.linux-x86_64-3.9/ftest-f2pywrappers2.f90:13:17:

   13 |                 subroutine tentimes(array_i)
      |                 1
Error: Unclassifiable statement at (1)
/tmp/tmpwd_qygau/src.linux-x86_64-3.9/ftest-f2pywrappers2.f90:15:39:

   15 |                 end subroutine tentimes
      |                                       1
Error: Expected label ‘example’ for END SUBROUTINE statement at (1)
/tmp/tmpwd_qygau/src.linux-x86_64-3.9/ftest-f2pywrappers2.f90:14:53:

   14 |                     real*8, dimension(:,:) :: array_i
      |                                                     1
Error: Array ‘array_i’ at (1) cannot have a deferred shape
error: Command "/usr/bin/gfortran -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops -I/tmp/tmpwd_qygau/src.linux-x86_64-3.9 -I/home/srj/.local/lib/python3.9/site-packages/numpy/core/include -I/usr/include/python3.9 -c -c /tmp/tmpwd_qygau/src.linux-x86_64-3.9/ftest-f2pywrappers2.f90 -o /tmp/tmpwd_qygau/tmp/tmpwd_qygau/src.linux-x86_64-3.9/ftest-f2pywrappers2.o" failed with exit status 1

Similar nested procedures work well in gfortran. Why does this error happen? How to rectify this?


Solution

  • EDIT: The better way is to use f2py is "the smart way"

    just doing this works well

    python -m numpy.f2py allocate.f90 -m ftest -h ftest.pyf && \
    python -m numpy.f2py -c ftest.pyf allocate.f90
    

    Since this bypasses generating FORTRAN interface.


    Okay, I confirmed it's a bug in f2py, the generated code in f2pywrappers2.f90 has an error

    f2py produces:

    !     -*- f90 -*-
    !     This file is autogenerated with f2py (version:1.21.2)
    !     It contains Fortran 90 wrappers to fortran functions.
    
          subroutine f2pywrapexample (array, f2py_array_d0, f2py_array_d1)
          integer f2py_array_d0
          integer f2py_array_d1
          real*8 array(f2py_array_d0,f2py_array_d1)
          interface
          
                subroutine example(array) 
                    real*8, dimension(:,:),intent(inout) :: array
                    subroutine tentimes(array) 
                        real*8, dimension(:,:) :: array
                    end subroutine tentimes
                end subroutine example
          end interface
          call example(array)
          end
    

    correct code:

          subroutine f2pywrapexample (array, f2py_array_d0, f2py_array_d1)
          integer f2py_array_d0
          integer f2py_array_d1
          real*8 array(f2py_array_d0,f2py_array_d1)
          interface
          
                subroutine example(array) 
                    real*8, dimension(:,:),intent(inout) :: array
                end subroutine example
                subroutine tentimes(array) 
                    real*8, dimension(:,:) :: array
                end subroutine tentimes
          end interface
          call example(array)
          end
    

    the interface shouldn't actually contain nested procedures, even though the subroutine has a nested procedure

    fixing this, then generating the corresponding object, placing it in the correct temp folder and manually linking with the command

    /usr/bin/gfortran -Wall -g -Wall -g -shared /tmp/tmpfw0ammli/tmp/tmpfw0ammli/src.linux-x86_64-3.9/ftestmodule.o /tmp/tmpfw0ammli/tmp/tmpfw0ammli/src.linux-x86_64-3.9/fortranobject.o /tmp/tmpfw0ammli/allocate.o /tmp/tmpfw0ammli/tmp/tmpfw0ammli/src.linux-x86_64-3.9/ftest-f2pywrappers2.o -L/usr/lib/gcc/x86_64-redhat-linux/11 -L/usr/lib/gcc/x86_64-redhat-linux/11 -L/usr/lib64 -lgfortran -o ./ftest.cpython-39-x86_64-linux-gnu.so
    

    this succeeded, and python prints correct output.

    enter image description here