首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Cython将np.ndarray传递给Fortran

使用Cython将np.ndarray传递给Fortran
EN

Stack Overflow用户
提问于 2020-06-02 14:37:45
回答 1查看 825关注 0票数 2

我正在用Python包装一个Fortran模块。我选择使用Cython来完成它。我的问题是将一个np.ndarray传递给Fortran。我能够从Fortran收到一个np.ndarray,但是我所有传递到Fortran的尝试都没有成功。

我发现问题直接发生在Cython接口上,因为我的Fotran子程序正常工作(就像它在没有数据的情况下一样工作)。Cython方面似乎也在正常工作,我可以在那里操作变量。

我的最低工作示例:

PATTERN_wrap.f90

代码语言:javascript
复制
module PATTERN_wrap
    use iso_c_binding, only: c_float, c_double, c_short, c_int
    implicit none

CONTAINS
    subroutine c_pattern(scalar_variable, array_variable, return_array) bind(c)
        implicit NONE

        INTEGER(c_int), intent(in) :: scalar_variable
        INTEGER(c_int), intent(in), DIMENSION(10, 15) :: array_variable

        REAL(c_float), INTENT(OUT), DIMENSION(10) :: return_array

        write(*,*) "start fortran"
        write(*,*) "scalar_variable"
        write(*,*) scalar_variable
        write(*,*) "array_variable"
        write(*,*) array_variable

        return_array = 3
        write(*,*) "end fortran"


!        call DO_PATTERN(&
!                scalar_variable=scalar_variable, &
!                array_variable=array_variable, &
!                return_array=return_array)
!
    end subroutine

end module PATTERN_wrap

注意:对实际做某事的子程序DO_PATTERN的调用被注释掉了,因为此时它与此无关。我只想指出上面的代码是一个包装器。

pattern.pyx

代码语言:javascript
复制
#cython: language_level=3
import cython
import numpy as np
cimport numpy as np

cdef extern:
    void c_pattern(
            int *scalar_variable,
            int *array_variable,
            float *return_array
    )

def run_pattern(
        int scalar_variable,
):
    cdef:
        np.ndarray[int, ndim=2, mode="fortran"] array_variable = np.ones((10,15), dtype=np.int32, order='F')
        np.ndarray[float, ndim=1, mode="fortran"] return_array = np.zeros(10, dtype=np.float32, order='F')

    c_pattern(
        &scalar_variable,
        &array_variable[0,0],
        &return_array[0],
    )

    print('Cython side')
    print(return_array)

    return return_array

setup.py

代码语言:javascript
复制
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy
npy_include_dir = numpy.get_include()

ext_modules = [Extension("pattern", ["pattern.pyx"],
                         include_dirs = [npy_include_dir],
                         libraries = ['gfortran', 'fftw3'], # need to include gfortran as a library
                         extra_link_args=[
                             "PATTERN_wrap.o"
                         ])]

setup(name = 'pattern',
      cmdclass = {'build_ext': build_ext},
      ext_modules = ext_modules)

我正在编译我的fortran代码

代码语言:javascript
复制
gfortran -Wall -fbounds-check -lm -g -fbacktrace  -fcheck=all -Wall -ffpe-trap=zero,invalid,overflow -fPIC -L/usr/lib/ -lfftw3 -L/usr/lib/ -lfftw3 -c PATTERN_wrap.f90

并使用python -m pip install .python setup.py build_ext --inplace编译Cython代码。这似乎没有任何区别。

我测试这个包:

代码语言:javascript
复制
$ python -c "import pattern; pattern.run_pattern(2);"
 start fortran
 scalar_variable
           2
 array_variable

 end fortran
Cython side
[3. 3. 3. 3. 3. 3. 3. 3. 3. 3.]

如您所见,标量正正确地传递给fortran,返回的数组也被正确地传递回Cython。唯一不起作用的是将数组从Cython传递到Fortran。简而言之,应该有一个二维数组的打印后的array_variable

除了上述MWE之外,我尝试了不同的方法:

(https://github.com/cython/cython/wiki/tutorials-NumpyPointerToC)

  • Creating变量作为Fortran int[::1,:] array_variable = np.ones((10,15), dtype=np.int32, order='F').

传递数组的

我所有的尝试都失败了,就像MWE一样。

我也试过使用头文件,没有什么不同。例如,这里使用了头文件:Fortran - Cython Workflow --这个问题本身不包含对我的问题的答案--只有标量才会传递给那里的Fortran。

我还想指出,在用f2py编译包时,相同的包装器加上所有底层文件都正常工作。子例程也在原来的Fortran程序内部工作。

编辑:

我的开发环境正在码头运行。基片图像是continuumio/miniconda3:4.8.2,另一方面是基于Debian的。我在那里测试了gfortran-8和gfortran-9,以及启用了fortran的hdf5编译器。结果总是一样的。

我决定用gcc/gfortran 7.50在我的主机系统Ubuntu18.04上运行我的测试。它确实正常工作。所以我去尝试不同的gcc版本。

我测试了图像:

  • gcc:7
  • gcc:8
  • gcc:9
  • gcc:10

运行它们时:

代码语言:javascript
复制
docker run --rm -v ~/minimum_working_example:/mwe -it gcc:7  /bin/bash

然后

代码语言:javascript
复制
apt update && apt install python3-pip -yy && cd /mwe && python3 -m pip install cython numpy && make && python3 setup.py build_ext --inplace && python3 -c "import pattern; pattern.run_pattern(2);" && rm -rf build/ *.so *.c *.mod *.o

在所有这些图像上,我的代码都正常工作。

EDIT2:

我只是在裸continuumio/miniconda3:4.8.2上运行测试,使用相同的测试命令(添加了apt安装gfortran,因为默认情况下没有fortran ),并且代码可以工作。

我重建了自己的形象,并以同样的方式进行了测试。不管用..。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-06-03 15:03:57

我设法找到了解决办法。,代码没问题,问题是我的配置。

正如我上面所描述的,我测试了gcc/gfortran的不同配置,看看这是否影响了细胞化。事实并非如此。因此,我开始分解我的Dockerfile,以便找到一个导致代码中断的步骤。原来是conda安装的numpy。

以上使用pip的ggc图像所做的所有测试:

代码语言:javascript
复制
$ python -m pip install numpy
Collecting numpy
  Downloading numpy-1.18.4-cp38-cp38-manylinux1_x86_64.whl (20.7 MB)
     |████████████████████████████████| 20.7 MB 18.9 MB/s
Installing collected packages: numpy
Successfully installed numpy-1.18.4

一个包裹,一个轮子,快速和容易。然而,我在我的“制作”形象中使用了conda。

如果您安装numpy by conda:

代码语言:javascript
复制
$ conda install numpy
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: /opt/conda

  added / updated specs:
    - numpy


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    blas-1.0                   |              mkl           6 KB
    intel-openmp-2020.1        |              217         780 KB
    libgfortran-ng-7.3.0       |       hdf63c60_0        1006 KB
    mkl-2020.1                 |              217       129.0 MB
    mkl-service-2.3.0          |   py38he904b0f_0          62 KB
    mkl_fft-1.0.15             |   py38ha843d7b_0         159 KB
    mkl_random-1.1.1           |   py38h0573a6f_0         341 KB
    numpy-1.18.1               |   py38h4f9e942_0           5 KB
    numpy-base-1.18.1          |   py38hde5b4d6_1         4.2 MB
    ------------------------------------------------------------
                                           Total:       135.5 MB

...

这里需要注意的是,除了numpy之外,conda还在安装libgfortran-ng-7.3.0。在我正在处理的图片中,安装了gcc/gfortran 8.5.0。

为什么这很重要?运行cython编译时:

代码语言:javascript
复制
$ python setup.py build_ext --inplace
running build_ext
cythoning pattern.pyx to pattern.c
building 'pattern' extension
creating build
creating build/temp.linux-x86_64-3.8
gcc -pthread -B /opt/conda/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/opt/conda/lib/python3.8/site-packages/numpy/core/include -I/opt/conda/include/python3.8 -c pattern.c -o build/temp.linux-x86_64-3.8/pattern.o
In file included from /opt/conda/lib/python3.8/site-packages/numpy/core/include/numpy/ndarraytypes.h:1832,
                 from /opt/conda/lib/python3.8/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,
                 from /opt/conda/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h:4,
                 from pattern.c:599:
/opt/conda/lib/python3.8/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning "Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
 #warning "Using deprecated NumPy API, disable it with " \
  ^~~~~~~
gcc -pthread -shared -B /opt/conda/compiler_compat -L/opt/conda/lib -Wl,-rpath=/opt/conda/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-3.8/pattern.o -lgfortran -o /mwe/pattern.cpython-38-x86_64-linux-gnu.so PATTERN_wrap.o

正如您在列表行中看到的那样,传递给gcc的包括/opt/conda/lib

代码语言:javascript
复制
$ ls /opt/conda/lib | grep "fortran"
libgfortran.so
libgfortran.so.4
libgfortran.so.4.0.0          

在这里,libgfortran,在我最初编译代码的不同版本中。

解决方案是:

代码语言:javascript
复制
$ conda install -c conda-forge libgfortran-ng==8.2.0

注意:使用conda -锻造通道是必要的,在我的例子中,conda无法仅从基本通道解决依赖关系。更重要的是,这个版本的libgfortran还需要将libblas从openblas版本更改为mkl,如果这与您有关的话。

通过这种方式,我在conda中安装了一个libgfortran,其主要版本与我在系统中使用的版本相同。在重新编译Cythonized之后,一切正常工作。

不管怎样,提防康达。

PS:谢谢@DawidW的反馈和测试我的代码。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62154557

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档