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.f90

然后,在python中:

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

IOW,似乎 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.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。

4

1 回答 1

16

在你的第一个例子中,我不知道你为什么说 f2py 似乎不接受双精度,什么时候test.test.r_dp 双精度。显示带小数点且没有显式 dtype 的值的 numpy 数组是双精度数组。

第二个示例显示了 F2PY 使用kind=<kind>. 请参阅常见问题解答: 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”,这是单精度。

有几种方法可以解决这个问题。

方法一

将类型声明分别更改为“real(kind=4)”和“real(kind=8)”(或“real 4”和“real 8”)。

当然,这违背了使用的目的selected_real_kind,并且对于某些编译器,4 和 8 不是单精度和双精度的正确 KIND 值。在这种情况下,使用 gfortran,sp是 4 和dp8,所以它可以工作。

方法二

告诉 f2py 如何处理这些声明。这在 f2py FAQ 中进行了解释,它是上面显示的 f2py 输出中的“getctype: ...”消息中建议的方法。

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

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

然后 f2py 将对这些real(sp)real(dp)声明做正确的事情。

方法三

它还可以重新排列您的代码:

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

有关类似建议,请参阅子程序参数未正确从 Python 传递到 Fortran 。

于 2012-09-21T04:46:45.847 回答