gpt4 book ai didi

python - Fortran - Cython 工作流程

转载 作者:IT老高 更新时间:2023-10-28 20:54:09 28 4
gpt4 key购买 nike

我想设置一个工作流,以便在 Windows 机器上使用 Cython 从 Python 访问 fortran 例程

经过一番搜索,我发现: http://www.fortran90.org/src/best-practices.html#interfacing-with-chttps://stackoverflow.com/tags/fortran-iso-c-binding/info

还有一些代码图片:

Fortran 端:

pygfunc.h:

void c_gfunc(double x, int n, int m, double *a, double *b, double *c);

pygfunc.f90

module gfunc1_interface
use iso_c_binding
use gfunc_module

implicit none

contains
subroutine c_gfunc(x, n, m, a, b, c) bind(c)
real(C_FLOAT), intent(in), value :: x
integer(C_INT), intent(in), value :: n, m
type(C_PTR), intent(in), value :: a, b
type(C_PTR), value :: c

real(C_FLOAT), dimension(:), pointer :: fa, fb
real(C_FLOAT), dimension(:,:), pointer :: fc

call c_f_pointer(a, fa, (/ n /))
call c_f_pointer(b, fb, (/ m /))
call c_f_pointer(c, fc, (/ n, m /))
call gfunc(x, fa, fb, fc)
end subroutine

end module

gfunc.f90

module gfunc_module

use iso_c_binding

implicit none
contains
subroutine gfunc(x, a, b, c)
real, intent(in) :: x
real, dimension(:), intent(in) :: a, b
real, dimension(:,:), intent(out) :: c

integer :: i, j, n, m
n = size(a)
m = size(b)
do j=1,m
do i=1,n
c(i,j) = exp(-x * (a(i)**2 + b(j)**2))
end do
end do
end subroutine
end module

Cython 方面:

pygfunc.pyx

cimport numpy as cnp
import numpy as np

cdef extern from "./pygfunc.h":
void c_gfunc(double, int, int, double *, double *, double *)

cdef extern from "./pygfunc.h":
pass

def f(float x, a=-10.0, b=10.0, n=100):
cdef cnp.ndarray ax, c
ax = np.arange(a, b, (b-a)/float(n))
n = ax.shape[0]
c = np.ndarray((n,n), dtype=np.float64, order='F')
c_gfunc(x, n, n, <double *> ax.data, <double *> ax.data, <double *> c.data)
return c

和设置文件:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np

ext_modules = [Extension('pygfunc', ['pygfunc.pyx'])]

setup(
name = 'pygfunc',
include_dirs = [np.get_include()],
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules )

所有文件都在一个目录中

fortran 文件编译(使用 NAG Fortran Builder)pygfunc 编译

但链接它们会引发:

错误 LNK2019:引用了未解析的外部符号 _c_gfunc 在函数___pyx_pf_7pygfunc_f

当然还有:

fatal error LNK1120:1 个 Unresolved external 问题

我错过了什么?还是这种在 Python 和 Fortran 之间建立工作流的方式从一开始就该死?

THX马丁

最佳答案

这是一个最低限度的工作示例。我使用 gfortran 并将编译命令直接写入安装文件。

gfunc.f90

module gfunc_module
implicit none
contains
subroutine gfunc(x, n, m, a, b, c)
double precision, intent(in) :: x
integer, intent(in) :: n, m
double precision, dimension(n), intent(in) :: a
double precision, dimension(m), intent(in) :: b
double precision, dimension(n, m), intent(out) :: c
integer :: i, j
do j=1,m
do i=1,n
c(i,j) = exp(-x * (a(i)**2 + b(j)**2))
end do
end do
end subroutine
end module

pygfunc.f90

module gfunc1_interface
use iso_c_binding, only: c_double, c_int
use gfunc_module, only: gfunc
implicit none
contains
subroutine c_gfunc(x, n, m, a, b, c) bind(c)
real(c_double), intent(in) :: x
integer(c_int), intent(in) :: n, m
real(c_double), dimension(n), intent(in) :: a
real(c_double), dimension(m), intent(in) :: b
real(c_double), dimension(n, m), intent(out) :: c
call gfunc(x, n, m, a, b, c)
end subroutine
end module

pygfunc.h

extern void c_gfunc(double* x, int* n, int* m, double* a, double* b, double* c);

pygfunc.pyx

from numpy import linspace, empty
from numpy cimport ndarray as ar

cdef extern from "pygfunc.h":
void c_gfunc(double* a, int* n, int* m, double* a, double* b, double* c)

def f(double x, double a=-10.0, double b=10.0, int n=100):
cdef:
ar[double] ax = linspace(a, b, n)
ar[double,ndim=2] c = empty((n, n), order='F')
c_gfunc(&x, &n, &n, <double*> ax.data, <double*> ax.data, <double*> c.data)
return c

setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
# This line only needed if building with NumPy in Cython file.
from numpy import get_include
from os import system

# compile the fortran modules without linking
fortran_mod_comp = 'gfortran gfunc.f90 -c -o gfunc.o -O3 -fPIC'
print fortran_mod_comp
system(fortran_mod_comp)
shared_obj_comp = 'gfortran pygfunc.f90 -c -o pygfunc.o -O3 -fPIC'
print shared_obj_comp
system(shared_obj_comp)

ext_modules = [Extension(# module name:
'pygfunc',
# source file:
['pygfunc.pyx'],
# other compile args for gcc
extra_compile_args=['-fPIC', '-O3'],
# other files to link to
extra_link_args=['gfunc.o', 'pygfunc.o'])]

setup(name = 'pygfunc',
cmdclass = {'build_ext': build_ext},
# Needed if building with NumPy.
# This includes the NumPy headers when compiling.
include_dirs = [get_include()],
ext_modules = ext_modules)

test.py

# A script to verify correctness
from pygfunc import f
print f(1., a=-1., b=1., n=4)

import numpy as np
a = np.linspace(-1, 1, 4)**2
A, B = np.meshgrid(a, a, copy=False)
print np.exp(-(A + B))

我所做的大部分更改都不是非常根本。以下是重要的。

  • 您混合了 double 和单精度 float 。 不要那样做。 同时使用 real (Fortran)、float (Cython) 和 float32 (NumPy),同时使用 double (Fortran)、double (Cyton) 和 float64 (NumPy)。尽量不要无意中混合它们。我假设你在我的例子中想要 double 。

  • 您应该将所有变量作为指针传递给 Fortran。在这方面,它与 C 调用约定不匹配。 Fortran 中的 iso_c_binding 模块仅匹配 C 命名约定。将数组作为指针传递,其大小作为单独的值。可能还有其他方法可以做到这一点,但我不知道。

我还在设置文件中添加了一些内容,以显示在构建时可以在哪里添加一些更有用的额外参数。

要编译,请运行 python setup.py build_ext --inplace。要验证它是否有效,请运行测试脚本。

这是 fortran90.org 上显示的示例:mesh_exp

这是我前段时间整理的另外两个:ftridiag , fssor我当然不是这方面的专家,但这些示例可能是一个很好的起点。

关于python - Fortran - Cython 工作流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22404060/

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