简体   繁体   English

f2py:在与 python 接口时在 fortran 中指定真正的精度?

[英]f2py: Specifying real precision in fortran when interfacing with python?

I am playing around with f2py.我在玩 f2py。 I'm a bit confused about numpy intrinsic types vs. fortran 90 types.我对 numpy 内在类型与 fortran 90 类型有点困惑。 It seems like I can only use single precision reals in fortran 90, when interacting with python.在与 python 交互时,我似乎只能在 fortran 90 中使用单精度实数。 Let me illustrate with an example:让我用一个例子来说明:

Say I have this fortran 90 module, test.f90, to be compiled with f2py and imported in python:假设我有这个 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

and I compile like this:我是这样编译的:

f2py -c -m test test.f90 f2py -c -m 测试 test.f90

Then, in python:然后,在python中:

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

IOW, it seems like f2py doesn't accept double precision. IOW,似乎 f2py 不接受双精度。 This becomes even more problematic when passing input to a fortran 90 subroutine from python.当将输入从 python 传递到 fortran 90 子例程时,这变得更加成问题。 Say I extend my module to:假设我将模块扩展到:

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 f2py -c -m 测试 test.f90

python 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 -1.15948430791165406E+155

So, it seems like any input variable to be sent from python must be declared single precision.因此,似乎从 python 发送的任何输入变量都必须声明为单精度。 Is this a known issue with f2py?这是 f2py 的已知问题吗?

Also, as a follow up question: Converting from sp to dp works, in the following sense:另外,作为后续问题:从 sp 转换为 dp 工作,在以下意义上:

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

But I wonder if this is compiler specific at all?但我想知道这是否是特定于编译器的? Can I expect the above subroutine to do the right thing with any compiler on any architecture?我可以期望上述子例程对任何架构上的任何编译器都做正确的事情吗? When testing, I used gfortran fro all the above examples.测试时,我在上述所有示例中都使用了 gfortran。

In your first example, I don't know why you say it seems like f2py doesn't accept double precision, when test.test.r_dp is double precision.在你的第一个例子中,我不知道你为什么说 f2py 似乎不接受双精度,当test.test.r_dp双精度时。 A numpy array that shows a value with a decimal point and no explicit dtype is a double precision array.显示带有小数点且没有显式 dtype 的值的 numpy 数组是双精度数组。

The second example shows a limitation in F2PY's handling of type definitions with kind=<kind> .第二个示例显示了 F2PY 处理kind=<kind>类型定义的限制。 See the FAQ: https://numpy.org/doc/stable/f2py/advanced.html#dealing-with-kind-specifiers请参阅常见问题解答: https : //numpy.org/doc/stable/f2py/advanced.html#dealing-with-kind-specifiers

To see what is happening, run f2py test.f90 -m test .要查看发生了什么,请运行f2py test.f90 -m test I get this:我明白了:

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"

Note that it is mapping both "real(kind=sp)" and "real(kind=dp)" to C "float", which is single precision.请注意,它将“real(kind=sp)”和“real(kind=dp)”都映射到C“float”,这是单精度。

There are several ways to fix this.有几种方法可以解决这个问题。

Method 1方法一

Change the type declarations to "real(kind=4)" and "real(kind=8)" (or "real 4" and "real 8"), respectively.将类型声明分别更改为“real(kind=4)”和“real(kind=8)”(或“real 4”和“real 8”)。

Of course, this defeats the purpose of using selected_real_kind , and for some compilers, 4 and 8 are not the correct KIND values for single and double precision.当然,这违背了使用selected_real_kind的目的,对于某些编译器,4 和 8 不是单精度和双精度的正确 KIND 值。 In this case, with gfortran, sp is 4 and dp is 8, so it works.在这种情况下,使用 gfortran, sp是 4, dp是 8,所以它可以工作。

Method 2方法二

Tell f2py how to handle those declarations.告诉 f2py 如何处理这些声明。 This is explained in the f2py FAQ, and it is the approach suggested in the "getctype: ..." messages in the output of f2py shown above.这在 f2py 常见问题解答中进行了解释,这是上面显示的 f2py 输出中的“getctype: ...”消息中建议的方法。

In this case, you would create a file called .f2py_f2cmap (in the directory where you are running f2py) that contains the line在这种情况下,您将创建一个名为.f2py_f2cmap的文件(在您运行 f2py 的目录中),其中包含以下行

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

Then f2py will do the right thing with those real(sp) and real(dp) declarations.然后 f2py 将使用那些real(sp)real(dp)声明做正确的事情。

Method 3方法三

It also works to rearrange your code a bit:它还可以稍微重新排列您的代码:

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

See Subroutine argument not passed correctly from Python to Fortran for a similar suggestion.有关类似建议,请参阅子例程参数未正确从 Python 传递到 Fortran

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM