简体   繁体   English

使用F2PY在numpy数组上使用Fortran函数时的结果不一致

[英]Inconsistent result when using Fortran function on numpy array with F2PY

I am trying to understand how F2PY works. 我想了解F2PY的工作原理。 To do so, I wrote a simple Fortran function which takes an array as input and returns the sum of the elements of the array. 为此,我编写了一个简单的Fortran函数,它将数组作为输入并返回数组元素的总和。

I wrote three different versions of the same functions, which I expect to hold the same result: 我写了三个不同版本的相同函数,我希望它们具有相同的结果:

function rsum(arr)
real, dimension(:), intent(in) :: arr
real :: rsum
rsum=sum(arr)
end function rsum

function rsum1(arr)
real(8), dimension(:), intent(in) :: arr
real(8) :: rsum1
rsum1=sum(arr)
end function rsum1

function rsum2(arr) result(s)
real, dimension(:), intent(in) :: arr
real :: s
s=sum(arr)
end function rsum2

function rsum3(arr) result(s)
real(8), dimension(:), intent(in) :: arr
real(8) :: s
s=sum(arr)
end function rsum3

My python script to test these functions is the following: 我测试这些函数的python脚本如下:

from numpy import *
import ftest as f

a=array(range(3))

print(f.rsum(a))
print(f.rsum1(a))
print(f.rsum2(a))
print(f.rsum3(a))

but the result is this: 但结果如下:

3.0
0.0
3.0
3.0

All the results are correct, except the one of rsum1 , which is 0.0 . 所有结果都是正确的,除了rsum1 ,即0.0 What I find even more strange is that rsum3 , in which I merely change the name of the result of the function (or, at least, I think I am doing so), works perfectly! 我发现更奇怪的是rsum3 ,我只是更改了函数结果的名称(或者,至少,我认为我这样做了),完美无缺!

I know this has something to do with the type conversion between Fortran and numpy, but I don't understand what the problem is. 我知道这与Fortran和numpy之间的类型转换有关,但我不明白问题是什么。

PS: I only learned Fortran very recently. PS:我最近才学会了Fortran。

Short answer and workaround 简短的回答和解决方法

The root cause of the problem is related the use of assumed-shape dummy arguments (ie arr ) in your functions. 问题的根本原因与在函数中使用假定形状的伪参数(即arr )有关。 Fortran requires such functions to have explicit interfaces. Fortran要求此类函数具有显式接口。 @VladimirF gave an excellent answer to your (related?) question here about just that, indicating that the preferred solution is to put the functions in a module. @VladimirF 在这里给出了关于你的(相关的?)问题的优秀答案,表明首选的解决方案是将函数放在一个模块中。 Assuming your code listing with functions is saved in a file called funcs.f90 , you can simply put them into a module, eg called mod_funcs.f90 , like so: 假设你的函数代码列表保存在一个名为funcs.f90的文件中,你可以简单地将它们放入一个模块中,例如名为mod_funcs.f90 ,如下所示:

module mod_funcs
    implicit none
    contains
        include "funcs.f90"
end module mod_funcs

Wrap this with F2PY python -m numpy.f2py -m ftest -c mod_funcs.f90 , update the import statement in your python test script to from ftest import mod_funcs as f and then run it to get the expected result: 用F2PY python -m numpy.f2py -m ftest -c mod_funcs.f90 ,将python测试脚本中的import语句更新为from ftest import mod_funcs as f ,然后运行它以获得预期结果:

3.0
3.0
3.0
3.0

The longer answer and explanation 更长的答案和解释

Fortran function s are wrapped in subroutine s by F2PY. Fortran function由F2PY包装在subroutine In order to support assumed-shape arrays in a Fortran standard compliant way, the F2PY created subroutine wrappers contain interface s for user defined functions with assumed-shape dummy arguments. 为了以符合Fortran标准的方式支持假定形状数组,F2PY创建的子例程包装器包含用于具有假定形状伪参数的用户定义函数的interface You can have a look at these wrappers by specifying a build directory with a --build-dir flag when wrapping with F2PY, eg like this: 您可以通过使用F2PY包装时使用--build-dir标志指定构建目录来查看这些包装器,例如:

python -m numpy.f2py --build-dir .\build -m ftest -c funcs.f90

Looking at the wrapper that is created for the problematic function rsum1 is revealing (I'm copying verbatim from ftest-f2pywrappers.f keeping F2PY's indentation): 看看为有问题的函数rsum1创建的包装器是rsum1的(我从ftest-f2pywrappers.f逐字复制,保持F2PY的缩进):

subroutine f2pywraprsum1 (rsum1f2pywrap, arr, f2py_arr_d0)
integer f2py_arr_d0
real(8) arr(f2py_arr_d0)
real(8) rsum1f2pywrap
interface
function rsum1(arr) 
    real(8), dimension(:),intent(in) :: arr
end function rsum1
end interface
rsum1f2pywrap = rsum1(arr)
end

Note that due to implicit data typing rules , the interface for rsum1 implies a function with real data type, not real(8) as intended - so there is a data type mismatch in the interface! 请注意,由于隐式数据类型规则rsum1interface意味着具有real数据类型的函数, 而不是预期的real(8) - 因此接口中存在数据类型不匹配! This explains why the seemingly identical function with an explicit result statement ( rsum3 ) returns the correct result in your original example, its result has the correct data type. 这解释了为什么具有显式result语句( rsum3 )的看似相同的函数在原始示例中返回正确的结果,其结果具有正确的数据类型。 By good fortune in naming your functions, rsum has the correct interface. 通过命名您的函数, rsum具有正确的接口。 If you change rsum 's name to eg isum , implicit data typing rules in its F2PY subroutine wrapper interface will imply that it has an integer result, and you will get the following output from your (modified to reflect the name change from fsum to isum ) python script: 如果将rsum的名称更改为例如isum ,则其F2PY子例程包装器界面中的隐式数据类型规则将暗示它具有integer结果,并且您将从中获得以下输出(已修改以反映从fsumisum的名称更改) )python脚本:

0.0
0.0
3.0
3.0

So to me it appears as if there may be a bug in how F2PY creates interfaces for functions with assumed-shape dummy arguments (which can be bypassed by putting those functions into a module directly, or by explicitly declaring return value of the function using result ). 所以对我来说似乎F2PY如何为具有假定形状伪参数的函数创建接口(可以通过将这些函数直接放入模块中,或通过使用result显式声明函数的返回值来绕过) )。

For completeness, I was using Python 3.6.3 :: Intel Corporation , with NumPy 1.14.3 , and GNU Fortran (GCC) 8.2.0 . 为了完整Python 3.6.3 :: Intel Corporation ,我使用的是Python 3.6.3 :: Intel CorporationNumPy 1.14.3GNU Fortran (GCC) 8.2.0

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

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