【问题标题】:Returning 0-length arrays from fortran to python using f2py使用 f2py 将 0 长度数组从 fortran 返回到 python
【发布时间】:2019-09-17 06:03:18
【问题描述】:

我正在使用f2py 为用fortran 编写的基于MPI 的库生成包装器。由于我使用的数组分区方案,在给定足够多的 MPI 进程的情况下,进程可能有一个长度为 0 的本地数组。这会在我有权访问的 Cray 系统上触发以下错误:

ValueError: failed to create intent(cache|hide)|optional array-- 
must have defined dimensions but got (0,)

我的桌面上没有收到相同的错误。这可能与我安装的 python 和 numpy 的版本有关。在我的桌面上它们是 numpy 版本 1.16.4 和 python 2.7.15+,在集群上它们是 numpy 1.13.3 和 python 2.7.14。由于我无法升级集群上的软件包,我想知道是否存在简单的解决方法。以下代码重现了错误:

文件'fortran_sub.f90':

subroutine sub(a_size, a)                                                       
                                                                            
    integer, intent(in) :: a_size                                               
    real, dimension(a_size), intent(out) :: a                                   
                                                                          
    if (size(a) > 0) then                                                       
        a = size(a)                                                             
    endif                                                                       
                                                                              
end subroutine sub  

使用 f2py 封装编译如下:

python2 -m numpy.f2py -h --overwrite-signature fortran_sub.pyf -m 
fortran_sub fortran_sub.f90

python2 -m numpy.f2py --f90exec="ftn" -c fortran_sub.pyf -m 
fortran_sub fortran_sub.f90

生成的.pyf是:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module fortran_sub ! in 
    interface  ! in :fortran_sub
        subroutine sub(a_size,a) ! in :fortran_sub:fortran_sub.f90
            integer intent(in) :: a_size
            real dimension(a_size),intent(out),depend(a_size) :: a
        end subroutine sub
     end interface 
end python module fortran_sub

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

使用 python2 pytest.py 运行以下 python 程序“pytest.py”:

import fortran_sub

a = fortran_sub.sub(2)
print(a)

a = fortran_sub.sub(1)
print(a)

a = fortran_sub.sub(0)
print(a)

我得到以下输出:

[ 2.  2.]
[ 1.]
Traceback (most recent call last):
  File "pytest.py", line 11, in <module>
   a = fortran_sub.sub(0)
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (0,)

【问题讨论】:

    标签: python numpy fortran f2py


    【解决方案1】:

    自从我知道ctypes 用于Python-Fortran 接口以来,我已经很久没有使用f2py 了。这是一个基于ctypes 的解决方案,它也可以处理零大小的数组(从技术上讲,它也应该适用于 MPI),

    module sub_mod
    contains
        subroutine sub(a_size, a) bind(C,name="sub")
            !DEC$ ATTRIBUTES DLLEXPORT :: sub
            use iso_c_binding, only: c_size_t, c_double
            integer(c_size_t), intent(in) :: a_size
            real(c_double), intent(inout) :: a(a_size)
            if (size(a) > 0) then
                a = size(a)
            endif
        end subroutine sub
    end module sub_mod
    

    用 Intel 或(其他编译器)编译它以创建 DLL 库,

    ifort /dll main.f90
    

    带有来自 ifort 的如下输出消息,

    Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417
    Copyright (C) 1985-2019 Intel Corporation.  All rights reserved.
    
    Microsoft (R) Incremental Linker Version 14.22.27905.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    -out:main.dll
    -dll
    -implib:main.lib
    main.obj
       Creating library main.lib and object main.exp
    

    然后可以通过如下 ctypes 从 Python 调用,

    #!/usr/bin/env python
    
    import ctypes as ct
    import numpy as np
    
    # import Fortran DLL
    lib = ct.CDLL("main.dll")
    lib.sub.restype = None    # define subroutine result type
    
    # define subroutine argument type
    lib.sub.argtypes =  [ ct.POINTER(ct.c_size_t)        # a_size
                        , ct.POINTER(ct.c_double)       # a array
                        ]
    # all Fortran DLL
    for i in [2,1,0]:
        a_size = i
        a = np.zeros(shape=[a_size])
        lib.sub ( ct.byref( ct.c_size_t(a_size) ) # Fortran passes everything around by reference
                , np.ctypeslib.as_ctypes( a ) # pointer to numpy array
                )
        print("a_size = {}, a = {}".format(a_size, a))
    

    给出以下内容,

    a_size = 2, a = [2. 2.]
    a_size = 1, a = [1.]
    a_size = 0, a = []
    

    【讨论】:

    • 感谢您的详细回答。不过,具有讽刺意味的是,在我确信 f2py 是一种更有效的做事方式之后,几个月前我实际上从 ctypes 切换了过来。我想我将来会坚持使用 ctypes,我更喜欢确切地知道发生了什么。不过,就目前而言,我希望有人有专门的 f2py 解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多