为什么当我更改结果变量的名称时,使用 f2py return 编译的 Fortran 函数为零?
Why does a Fortran function compiled with f2py return zero when I change the name of the result variable?
当我调用以下函数时,它 returns 1
正如预期的那样:
integer function my_func() result(myresult)
myresult = 1
end function my_func
但是当我将 return 值的名称修改为以字母 "r" 开头,然后是函数 returns 0
。
integer function my_func() result(rresult)
rresult = 1
end function my_func
这是什么原因造成的?我的第一个想法是它与隐式类型有关,但该函数位于指定 implicit none
的模块中。
这是完整的模块
module my_mod
implicit none
contains
integer function my_func() result(myresult)
myresult = 1
end function my_func
end module my_mod
我正在使用 Fortran 90 并使用 gfortran 进行编译。
编辑
这里有一个完整的程序来演示这个问题
生成文件:
.PHONY: pytest clean
CYTHON_LIB = fortran_mods.cpython-37m-x86_64-linux-gnu.so
FFLAGS += -fdefault-real-8
pytest: $(CYTHON_LIB)
./tests.py
$(CYTHON_LIB): my_mod.F90
f2py -c -m fortran_mods my_mod.F90 --f90flags="$(FFLAGS)"
clean:
rm *.so
my_mod.F90:
module my_mod
implicit none
contains
!********************************************************
integer function my_func_without_r() result(myresult)
myresult = 1
end function
integer function my_func_with_r() result(rresult)
rresult = 1
end function
end module my_mod
tests.py
#!/usr/bin/env python3
import fortran_mods
from fortran_mods import *
print("with r:", my_mod.my_func_with_r())
print("without r:", my_mod.my_func_without_r())
当 make pytest
为 运行 且 FFLAGS += -fdefault-real-8
包含在 Makefile 中时的输出为
with r: 0.0
without r: 1
否则为
with r: 1.0
without r: 1
这里的问题肯定是 F2PY 如何包装 Fortran 函数,而不是 Fortran 代码本身。
为了更深入地了解 F2PY 如何包装一个函数(尤其是当事情没有按预期进行时),将过程分成几个部分总是有帮助的(参见 The smart way)。因此,首先创建一个签名文件,让您可以查看 F2PY 如何解释您的代码。对于您的特定示例,运行:
f2py -m fortran_mods -h my_mod.pyf my_mod.F90
这将生成一个签名文件 my_mod.pyf
看起来像这样:
python module fortran_mods ! in
interface ! in :fortran_mods
module my_mod ! in :fortran_mods:my_mod.F90
function my_func_without_r() result (myresult) ! in :fortran_mods:my_mod.F90:my_mod
integer :: myresult
end function my_func_without_r
function my_func_with_r() result (rresult) ! in :fortran_mods:my_mod.F90:my_mod
real :: rresult
end function my_func_with_r
end module my_mod
end interface
end python module fortran_mods
显然 F2PY 将 my_func_with_r
的结果变量 rresult
错误识别为 real
。您可以简单地将 real :: rresult
替换为 my_mod.pyf
中的预期 integer :: rresult
,执行 F2PY 包装的 next/second 步骤,并使用更正后的签名文件进行编译:
f2py -c my_mod.pyf my_mod.F90
您的 python 脚本现在应该给出预期的输出。
如果您有很多函数要包装,则可能不需要这种修改签名文件的方法。可能导致 F2PY 困难的是您的函数定义使用结果变量,而它们的类型定义没有出现在函数主体中(即 F2PY 问题,而不是 Fortran 问题)。如果您将函数定义更改为例如:
function my_func_with_r() result(rresult)
integer :: rresult
rresult = 1
end function
或
integer function my_func_with_r()
my_func_with_r = 1
end function
您可以像原来那样一步完成 F2PY 换行,并且应该仍能得到正确的输出。
最后,我将对评论中提出的问题再投一票:用 F2PY 包装函数时使用 -fdefault-real-8
是自找麻烦。到 make a Fortran procedure callable from Python,F2PY 创建:
a Python C/API extension module (called as wrapper module) that
implements a Python extension function (written in C, called as
wrapper function) which in turn calls the given Fortran procedure.
整个过程基于 F2PY 如何解释 Fortran 源代码中的数据类型 - 如果源代码是使用更改声明数据类型的编译器标志编译的,那么事情注定会被破坏(直接类似于 real
/integer
与您原来的问题不匹配)。因此,您的变量和函数的数据类型应在 Fortran 代码本身中明确设置,请参阅与 F2PY 相关的 here and here about Fortran's kind
parameter in general, and here。
当我调用以下函数时,它 returns 1
正如预期的那样:
integer function my_func() result(myresult)
myresult = 1
end function my_func
但是当我将 return 值的名称修改为以字母 "r" 开头,然后是函数 returns 0
。
integer function my_func() result(rresult)
rresult = 1
end function my_func
这是什么原因造成的?我的第一个想法是它与隐式类型有关,但该函数位于指定 implicit none
的模块中。
这是完整的模块
module my_mod
implicit none
contains
integer function my_func() result(myresult)
myresult = 1
end function my_func
end module my_mod
我正在使用 Fortran 90 并使用 gfortran 进行编译。
编辑
这里有一个完整的程序来演示这个问题
生成文件:
.PHONY: pytest clean
CYTHON_LIB = fortran_mods.cpython-37m-x86_64-linux-gnu.so
FFLAGS += -fdefault-real-8
pytest: $(CYTHON_LIB)
./tests.py
$(CYTHON_LIB): my_mod.F90
f2py -c -m fortran_mods my_mod.F90 --f90flags="$(FFLAGS)"
clean:
rm *.so
my_mod.F90:
module my_mod
implicit none
contains
!********************************************************
integer function my_func_without_r() result(myresult)
myresult = 1
end function
integer function my_func_with_r() result(rresult)
rresult = 1
end function
end module my_mod
tests.py
#!/usr/bin/env python3
import fortran_mods
from fortran_mods import *
print("with r:", my_mod.my_func_with_r())
print("without r:", my_mod.my_func_without_r())
当 make pytest
为 运行 且 FFLAGS += -fdefault-real-8
包含在 Makefile 中时的输出为
with r: 0.0
without r: 1
否则为
with r: 1.0
without r: 1
这里的问题肯定是 F2PY 如何包装 Fortran 函数,而不是 Fortran 代码本身。
为了更深入地了解 F2PY 如何包装一个函数(尤其是当事情没有按预期进行时),将过程分成几个部分总是有帮助的(参见 The smart way)。因此,首先创建一个签名文件,让您可以查看 F2PY 如何解释您的代码。对于您的特定示例,运行:
f2py -m fortran_mods -h my_mod.pyf my_mod.F90
这将生成一个签名文件 my_mod.pyf
看起来像这样:
python module fortran_mods ! in
interface ! in :fortran_mods
module my_mod ! in :fortran_mods:my_mod.F90
function my_func_without_r() result (myresult) ! in :fortran_mods:my_mod.F90:my_mod
integer :: myresult
end function my_func_without_r
function my_func_with_r() result (rresult) ! in :fortran_mods:my_mod.F90:my_mod
real :: rresult
end function my_func_with_r
end module my_mod
end interface
end python module fortran_mods
显然 F2PY 将 my_func_with_r
的结果变量 rresult
错误识别为 real
。您可以简单地将 real :: rresult
替换为 my_mod.pyf
中的预期 integer :: rresult
,执行 F2PY 包装的 next/second 步骤,并使用更正后的签名文件进行编译:
f2py -c my_mod.pyf my_mod.F90
您的 python 脚本现在应该给出预期的输出。
如果您有很多函数要包装,则可能不需要这种修改签名文件的方法。可能导致 F2PY 困难的是您的函数定义使用结果变量,而它们的类型定义没有出现在函数主体中(即 F2PY 问题,而不是 Fortran 问题)。如果您将函数定义更改为例如:
function my_func_with_r() result(rresult)
integer :: rresult
rresult = 1
end function
或
integer function my_func_with_r()
my_func_with_r = 1
end function
您可以像原来那样一步完成 F2PY 换行,并且应该仍能得到正确的输出。
最后,我将对评论中提出的问题再投一票:用 F2PY 包装函数时使用 -fdefault-real-8
是自找麻烦。到 make a Fortran procedure callable from Python,F2PY 创建:
a Python C/API extension module (called as wrapper module) that implements a Python extension function (written in C, called as wrapper function) which in turn calls the given Fortran procedure.
整个过程基于 F2PY 如何解释 Fortran 源代码中的数据类型 - 如果源代码是使用更改声明数据类型的编译器标志编译的,那么事情注定会被破坏(直接类似于 real
/integer
与您原来的问题不匹配)。因此,您的变量和函数的数据类型应在 Fortran 代码本身中明确设置,请参阅与 F2PY 相关的 here and here about Fortran's kind
parameter in general, and here。