f2py:在与Python接口时指定Fortran中的实数精度?

14

我正在使用f2py进行尝试。我对NumPy内置类型和Fortran 90类型有些困惑。当与Python交互时,似乎我只能在Fortran 90中使用单精度实数。让我用一个例子来说明:

假设我有这个Fortran 90模块test.f90,希望使用f2py编译并在Python中导入:

module test
implicit none

integer, parameter :: sp = selected_real_kind(6,37) ! single precision
integer, parameter :: dp = selected_real_kind(15,307) ! double precision

real(sp) :: r_sp = 1.0
real(dp) :: r_dp = 1.0_dp
end module

我这样编译:

f2py -c -m test test.f90

然后在Python中使用:

>>> import test
>>> test.test.r_sp
array(1.0, dtype=float32)
>>> test.test.r_dp
array(1.0)

换句话说,看起来f2py不接受双精度。当从Python传递输入到Fortran 90子例程时,这变得更加棘手。假设我将我的模块扩展为:

module test

implicit none
integer, parameter :: sp = selected_real_kind(6,37) ! single precision
integer, parameter :: dp = selected_real_kind(15,307) ! double precision

real(sp) :: r_sp = 1.0
real(dp) :: r_dp = 1.0_dp

contains 

subroutine input_sp(val)
  real(sp), intent(in) :: val
  real(sp) :: x
  x = val
  write(*,*) x
end subroutine

subroutine input_dp(val)
  real(dp), intent(in) :: val
  real(dp) :: x
  x = val
  write(*,*) x
end subroutine
end module

f2py -c -m test test.f90

Python

>>> import test
>>> test.test.input_sp(array(1.0,dtype=float32))
1.0000000    
>>> test.test.input_sp(array(1.0,dtype=float64))
1.0000000    
>>> test.test.input_dp(array(1.0,dtype=float32))
-1.15948430791165406E+155
>>> test.test.input_dp(array(1.0,dtype=float64))

-1.15948430791165406E+155

看起来好像从Python发送的任何输入变量都必须声明为单精度。这是f2py已知的问题吗?

另外,作为后续问题:从sp转换为dp可以按以下方式完成:

subroutine input_sp_to_dp(val)
  real(sp), intent(in) :: val(2)
  real(dp) :: x(2)
  x = val
  write(*,*) x
end subroutine

但我想知道这是否与编译器有关?我能否期望上面的子程序在任何架构上的任何编译器上都能正常运行?在测试时,我使用gfortran进行了所有上述示例。

1个回答

16
在您的第一个例子中,我不知道为什么您说似乎f2py不接受双精度,因为test.test.r_dp 双精度。显示带有小数点且没有显式数据类型的numpy数组是双精度数组。
第二个例子显示了F2PY处理带有kind=<kind>类型定义的限制。请参阅FAQ: https://numpy.org/doc/stable/f2py/advanced.html#dealing-with-kind-specifiers 要查看正在发生的情况,请运行f2py test.f90 -m test。我得到了这个:
Reading fortran codes...
    Reading file 'test.f90' (format:free)
Post-processing...
    Block: test
            Block: test
                Block: input_sp
                Block: input_dp
Post-processing (stage 2)...
    Block: test
        Block: unknown_interface
            Block: test
                Block: input_sp
                Block: input_dp
Building modules...
    Building module "test"...
        Constructing F90 module support for "test"...
          Variables: r_dp sp r_sp dp
            Constructing wrapper function "test.input_sp"...
getctype: "real(kind=sp)" is mapped to C "float" (to override define dict(real = dict(sp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=sp)" is mapped to C "float" (to override define dict(real = dict(sp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=sp)" is mapped to C "float" (to override define dict(real = dict(sp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
              input_sp(val)
            Constructing wrapper function "test.input_dp"...
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
              input_dp(val)
    Wrote C/API module "test" to file "./testmodule.c"
    Fortran 90 wrappers are saved to "./test-f2pywrappers2.f90"

注意,它将 "real(kind=sp)" 和 "real(kind=dp)" 都映射为 C 语言的 "float" 类型,即单精度。有几种方法可以修复这个问题。

方法1

将类型声明更改为 "real(kind=4)" 和 "real(kind=8)"(或者分别更改为 "real4" 和 "real8")。

当然,这样做会破坏使用 selected_real_kind 的目的,并且对于某些编译器,4和8不是单精度和双精度的正确 KIND 值。在这种情况下,使用gfortran,sp 是4,dp 是8,因此可以使用这种方法。

方法2

告诉 f2py 如何处理这些声明。在 f2py FAQ 中有解释,而且在上述 f2py 输出中显示的“getctype:...”消息中建议采用这种方法。

在这种情况下,您需要创建一个名为 .f2py_f2cmap 的文件(位于运行 f2py 的目录中),其中包含一行:

dict(real=dict(sp='float', dp='double'))

然后f2py将会对那些 real(sp)real(dp) 声明做正确的处理。

方法3

稍微调整一下你的代码也可以实现:

module types

implicit none
integer, parameter :: sp = selected_real_kind(6,37) ! single precision
integer, parameter :: dp = selected_real_kind(15,307) ! double precision

real(sp) :: r_sp = 1.0
real(dp) :: r_dp = 1.0_dp

end module


module input

contains 

subroutine input_sp(val)
  use types
  real(sp), intent(in) :: val
  real(sp) :: x
  x = val
  write(*,*) x
end subroutine

subroutine input_dp(val)
  use types
  real(dp), intent(in) :: val
  real(dp) :: x
  x = val
  write(*,*) dp, val, x
end subroutine

end module

请查看Subroutine argument not passed correctly from Python to Fortran获取类似建议。


感谢您的回答。我首先尝试了方法2,效果很好。但是后来我尝试将其应用于我正在编写的实际程序,该程序使用distutils通过setup.py文件进行编译等操作。然后我发现仅拥有.f2py_cmap文件是不够的,即使它在构建过程中成功应用了.f2py_cmap所做的更改。因此,我不得不使用方法3。实际上,我已经在使用一个单独的模块来定义精度变量,但我将“use types”语句放在了使用它的模块的顶部,而不是放在各个子例程内部。 - arne

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