我正在玩 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 测试测试.f90
然后,在 python 中:
>>> import test
>>> test.test.r_sp
array(1.0, dtype=float32)
>>> test.test.r_dp
array(1.0)
IOW,似乎 f2py 不接受 double 。当从 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 测试测试.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。
最佳答案
在你的第一个例子中,我不知道你为什么说 f2py 似乎不接受 double ,当 test.test.r_dp
是 double 。显示带小数点且没有显式 dtype 的值的 numpy 数组是 double 组。
第二个示例显示了 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)”(或“real4”和“real8”), 分别。
当然,这违背了使用 selected_real_kind
的目的。 ,对于某些编译器,
4 和 8 不是单精度和 double 的正确 KIND 值。
在这种情况下,使用 gfortran,sp
是 4 和 dp
是 8,所以它有效。
方法二
告诉 f2py 如何处理这些声明。这在 f2py 常见问题解答中进行了解释,它是上面显示的 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
参见 Subroutine argument not passed correctly from Python to Fortran对于类似的建议。
关于python - f2py:在与 python 交互时在 fortran 中指定真正的精度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12523524/