gpt4 book ai didi

python - 使用 Cython 将 np.ndarray 传递给 Fortran

转载 作者:行者123 更新时间:2023-12-02 18:18:00 25 4
gpt4 key购买 nike

我正在用 Python 包装一个 Fortran 模块。我选择使用 Cython 来做到这一点。我的问题是通过 np.ndarray到 Fortran。我能够收到 np.ndarray来自 Fortran,但我所有传递给 Fortran 的尝试都没有奏效。

我发现问题直接出在 Cython - Fortran 接口(interface)上,因为我的 Fotran 子例程工作正常(尽可能在没有数据的情况下工作)。 Cython 方面似乎也工作正常,我可以在那里操纵变量。

我的最小工作示例:
PATTERN_wrap.f90

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
#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
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 代码
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 . 编译 Cython 代码或 python setup.py build_ext --inplace .这似乎没有任何区别。

我测试了这个包:

$ 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,我尝试了不同的方法:
  • 使用 <int*> array_variable.data 传递数组
    Cython ( https://github.com/cython/cython/wiki/tutorials-NumpyPointerToC )
  • 不鼓励这样做
  • 创建变量作为 Fortran 连续 MemoryView int[::1,:] array_variable = np.ones((10,15), dtype=np.int32, order='F') .

  • 我所有的尝试都以与 MWE 相同的方式失败。

    我也尝试使用头文件,没有任何区别。例如这里使用了头文件: Fortran - Cython Workflow这个问题本身不包含我的问题的答案 - 只有标量传递给 Fortran 那里。

    我还想指出,当我使用 f2py 编译包时,相同的包装器和所有底层文件都可以正常工作。该子例程也适用于原始 Fortran 程序。

    编辑:

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

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

    我测试了图像:
  • gcc:7
  • gcc:8
  • gcc:9
  • gcc:10

  • 运行它们:
    docker run --rm -v ~/minimum_working_example:/mwe -it gcc:7  /bin/bash

    进而

    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 install gfortran,因为默认情况下没有 fortran)并且代码有效。

    我重建了我的图像并以相同的方式进行了测试。它不起作用...

    最佳答案

    我设法找到了解决方案。 代码没问题。 问题是我的配置。
    如上所述,我测试了 gcc/gfortran 的不同配置,看看这是否会影响 Cythonizing。不是。
    因此,我着手反汇编我的 Dockerfile,以找到导致代码中断的步骤。事实证明,这是 conda 安装的 numpy。
    我在上面使用 pip 对 ggc 图像进行的所有测试:

    $ 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。
    如果你通过 conda 安装 numpy:
    $ 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 编译时:
    $ 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 .
    $ ls /opt/conda/lib | grep "fortran"
    libgfortran.so
    libgfortran.so.4
    libgfortran.so.4.0.0
    在这里, libgfortran ,在我最初编译我的代码的不同版本中。
    解决方案是:
    $ conda install -c conda-forge libgfortran-ng==8.2.0
    注意:使用 conda-forge channel 是必要的,在我的情况下,conda 无法仅解决来自基本 channel 的包的依赖关系。更重要的是,这个版本的 libgfortran-ng 还需要将 libblas 从 openblas 版本更改为 mkl,如果这与您有关的话。
    通过这种方式,我在 conda 中安装了一个 libgfortran,它与我在系统中使用的主要版本相同。重新运行 Cythonized 包的编译后,一切正常。
    无论如何,当心 conda 。
    PS:感谢@DawidW 的反馈和测试我的代码。

    关于python - 使用 Cython 将 np.ndarray 传递给 Fortran,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62154557/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com