我正在用Python包装Fortran模块。我选择了使用Cython。我的问题是将np.ndarray
传递给Fortran。我可以从Fortran接收np.ndarray
,但是我所有的尝试都没有成功
我发现,问题直接出在Cython-Fortran接口上,因为我的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 .
或python setup.py build_ext --inplace
编译Cython代码。这似乎没有任何区别
我测试包:
$ 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
之后应该有一个2D数组
除了上述MWE,我还尝试了不同的方法:
用<int*> array_variable.data
传递数组
Cython(https://github.com/cython/cython/wiki/tutorials-NumpyPointerToC)不鼓励这样做
将变量创建为Fortran连续内存视图int[::1,:] array_variable = np.ones((10,15), dtype=np.int32, order='F')
我所有的尝试都以和MWE一样的方式失败了
我也尝试过使用头文件,没有任何区别。例如,这里使用了头文件:Fortran - Cython Workflow这个问题本身不包含我的问题的答案-只有标量传递给Fortran
我还想指出,当我用f2py编译包时,相同的包装器加上所有底层文件都工作正常。该子程序也在原始Fortran程序中工作
编辑:
我的开发环境正在docker中运行。baseimage是continuumio/miniconda3:4.8.2
,另一方面它基于Debian Buster。
我在那里测试了gfortran-8和gfortran-9以及启用fortran的hdf5编译器。结果总是一样的
我决定在我的主机系统Ubuntu18.04和gcc/gfortran 7.50上运行我的测试。它确实工作正常。所以我尝试了不同的gcc版本
我测试了图像:
使用以下工具运行它们:
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:
我只是在barecontinuumio/miniconda3:4.8.2
上运行了测试,使用了相同的测试命令(添加了apt install gfortran,因为默认情况下没有fortran),代码正常工作
我重建了我的图像,并以同样的方式进行了测试。它不起作用
我设法找到了解决办法代码正常。问题在于我的配置
如上所述,我测试了gcc/gfortran的不同配置,看看这是否影响了cythonization。事实并非如此。 因此,我继续分解Dockerfile,以找到导致代码中断的步骤。原来是康达安装了numpy
我使用pip对ggc图像进行的上述所有测试:
一个包装,一个轮子,方便快捷。然而,我在我的“产品”形象中使用了康达
如果您通过conda安装numpy:
这里需要注意的重要一点是,除了numpy之外,conda也在安装
libgfortran-ng-7.3.0
。在我正在处理的图像中,安装了gcc/gfortran 8.5.0为什么这很重要?运行cython编译时:
正如您在列表行中所看到的,传递给gcc的包含中有
/opt/conda/lib
这里是
libgfortran
,在我最初编译代码时使用的不同版本中解决方案是:
注意:使用conda forge通道是必要的,在我的例子中,conda无法仅从基本通道解析与包的依赖关系。更重要的是,这个版本的libgfortran ng还需要将libblas从openblas版本更改为mkl,如果这与您有关的话
通过这种方式,我在conda中安装了一个libgfortran,其主要版本与我在系统中使用的版本相同。在重新运行Cythonized包的编译后,一切都正常工作
无论如何,当心康达
PS:感谢@DawidW提供反馈并测试我的代码
相关问题 更多 >
编程相关推荐