当使用f2py时,Fortran模块中的函数作用域与编译为Fortran程序时不同?

4
我遇到的问题是使用f2py编译时,一些模块变量无法被模块内定义的函数识别。这些错误会在声明传递给函数的参数的变量类型(例如描述实际real类型或维度元素的变量)时引发。但是,使用gfortran编译时没有出现此错误。有什么区别,并且如何纠正使用f2py编译时的这些错误?
我的示例文件moddata.f90包含以下代码:
module mod

  implicit none

  integer, parameter :: dp = selected_real_kind(15)
  integer, parameter :: nelem = 3
  real(kind=dp), dimension(nelem) :: b

  parameter (b=(/3,1,2/))

contains
  function foo(x,y) result(z)
!  dp, nelem are defined in module above
    real(kind=dp), intent(in) :: x !scalar
    integer, dimension(nelem), intent(in) :: y
!  local variable
    real(kind=dp) :: z

    z = sum(b*y*x)

  end function foo
end module mod

我使用以下命令进行编译:

f2py -c -m moddata moddata.f90

我得到了以下错误:

  y_Dims[0]=(nelem);
             ^
1 warning and 1 error generated.reduce to a constant expression

如果在 integer, dimension(nelem), intent(in) :: y 之前重新定义 integer, parameter :: nelem=3 并重新编译,会得到以下错误:

      real(kind=dp) foof2pywrap
                1
Error: Parameter 'dp' at (1) has not been declared or is a variable, which does not reduce to a constant expression

对于每个real(kind=dp)声明都具有相同的错误,并且...
      foof2pywrap = foo(x, y)
                    1
Warning: Possible change of value in conversion from REAL(8) to REAL(4) at (1)

因此,我需要在函数中通过 integer, parameter :: dp = selected_real_kind(15) 重新定义 dp。然后就可以工作了。

当我使用 Fortran 包装器编译这个模块时,我不会遇到这些错误。我想知道为什么函数中的 nelemdp 在 f2py 中没有适当地分配作用域?

1个回答

4
也许我错了,但是我认为f2py无法处理Fortran 90的module+contains特性。如果你将你的代码转换成
function foo(x,y) result(z)
   integer, parameter :: dp = selected_real_kind(15)
   real(kind=dp), intent(in) :: x
   integer, parameter :: nelem = 3
   integer, dimension(3), parameter :  = (/3, 1, 2/)
   integer, dimension(nelem), intent(in) :: y
   real(kind=dp) :: z

   z = sum(b*y*x)
end function

如果像以前一样编译,它就可以工作:

 >>> x = 1.0000000000
 >>> y = [2, 3, 4]
 >>> moddata.foo(x,y)
 17.0

编辑

这个问题的答案说f2py不知道如何将Fortran函数转换为Python函数。因此,我将function foo改为subroutine foo2,然后编译为f2py moddata.f90 -m moddata,并得到以下输出:

Reading fortran codes...
        Reading file 'moddata.f90' (format:free)
Post-processing...
        Block: moddata
                        Block: moddata
In: :moddata:moddata.f90:moddata
get_parameters: got "invalid syntax (<string>, line 1)" on '(/3, 1, 2/)'
                                Block: foo2
Post-processing (stage 2)...
        Block: moddata
                Block: unknown_interface
                    Block: moddata
                            Block: foo2
Building modules...
        Building module "moddata"...
                Constructing F90 module support for "moddata"...
                    Variables: nelem b dp
                    Constructing wrapper function "moddata.foo2"...
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /home/jdwood/Documents/Physics/Fortran/tests/f2py/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /home/jdwood/Documents/Physics/Fortran/tests/f2py/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /home/jdwood/Documents/Physics/Fortran/tests/f2py/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /home/jdwood/Documents/Physics/Fortran/tests/f2py/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /home/jdwood/Documents/Physics/Fortran/tests/f2py/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /home/jdwood/Documents/Physics/Fortran/tests/f2py/.f2py_f2cmap file).
                          z = foo2(x,y)
Wrote C/API module "moddata" to file "./moddatamodule.c"
Fortran 90 wrappers are saved to "./moddata-f2pywrappers2.f90"

所以看起来双精度被丢失了,按照编辑一个叫做.f2py_f2cmap的文件的建议,我这样做了,对于dp没有错误。但是,仍然会出现nelem的错误,因此我可以想到两个解决方案:
  1. 继续使用3代替nelem
  2. nelemb作为变量传递给子程序
当使用parameter(b = (/3.d0, 1.d0, 2.d0/))时,我也发现自己收到了一个警告:
analyzeline: Failed to evaluate '/3.e0+1j*( 1.e0+1j*( 2.e0/)'. Ignoring: invalid syntax (<string>, line 1)

我不确定如何理解这句话。但是,当我使用x=1.0y=(/ 3, 6, 2/)(无论是在Python中还是在Fortran程序中使用模块),都会得到18的答案。

简而言之,在使用f2py时,请完全避免使用函数


我认为那不正确。这里有使用模块的例子:https://dev59.com/pmcs5IYBdhLWcg3w3HkG 和 http://cens.ioc.ee/projects/f2py2e/usersguide/#fortran-90-module-data。我也只包含了一个小例子,但我需要从许多函数和子程序中访问模块参数,而不仅仅是像我在简化的示例中所显示的 foo - hatmatrix
另外,我已经编辑了文本,以使其更清晰:如果我在函数中添加dpnelem的附加声明,则它可以工作,因此模块+包含表单确实有效。 - hatmatrix
@crippledlambda 就像我说的,“也许我错了”,但我可能发现了一些东西。马上更新我的答案。 - Kyle Kanos
不,没事。我会再深入挖掘一下。 - Kyle Kanos
感谢您挖掘这些信息。只要在函数中定义nelemdp,我就能让我的函数运行起来,而不必将其转换为子程序。我能够访问b而无需重新定义它。最好的情况是,这种行为是不可预测的。好吧,f2py结果有点令人失望。 - hatmatrix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接