Fortran - Cython工作流程 [英] Fortran - Cython Workflow
问题描述
我想建立一个工作流程,在Windows机器上使用Cython从Python到达Fortran例程
经过一番搜索后,我发现: 和一些代码片段: Fortran方面: pygfunc.h: pygfunc.f90 gfunc.f90 Cython方面: pygfunc.pyx 和设置文件: 所有文件在一个目录中 Fortran文件编译(使用NAG Fortran Builder)pygfunc编译 但链接它们会抛出: 错误LNK2019:无法解析的外部符号_c_gfunc引用 以及当然: 致命错误LNK1120:1个未解析的外部元素 我错过了什么?或者是这种方式来建立一个从一开始就在Python和Fortran之间的工作流? THX 这是一个最小的工作示例。
http://www.fortran90.org/src/best-practices。 html#与c ++接口和 https://stackoverflow.com/tags/fortran-
void c_gfunc(double x,int n ,int m,double * a,double * b,double * c);
模块gfunc1_interface
使用iso_c_binding
使用gfunc_module
隐式无
包含
子例程c_gfunc(x,n, m,a,b,c)bind(c)
real(C_FLOAT),intent(in),value :: x
整数(C_INT),intent(in),value :: n,m
类型(C_PTR),intent(in),value :: a,b
类型(C_PTR),value :: c
real(C_FLOAT),dimension(:) ,指针:: fa,fb
real(C_FLOAT),dimension(:, :),pointer :: fc
call c_f_pointer(a,fa,(/ n /))
调用c_f_pointer(b,fb,(/ m /))
调用c_f_pointer(c,fc,(/ n,m /))
调用gfunc(x,fa,fb,fc)
结束子程序
结束模块
模块gfunc_module
使用iso_c_binding
隐式无
包含
子程序gfunc(x,a,b,c)
实数,意图(in):: x
实数,维数(:),intent(in):: a,b
real,维度(:,:),意图(出):: c
整数:: i,j,n,m
n =大小(a)
m =大小(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
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,(ba)/ 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)
返回c
from distutils.core import setup $ b $ from distutils.extension从Cython.Distutils导入扩展
导入build_ext
import numpy作为np
ext_modules = [Extension('pygfunc',['pygfunc.pyx'])]
setup(
name ='pygfunc',
include_dirs = [np.get_include()],
cmdclass = {'build_ext':build_ext},
ext_modules = ext_modules)
函数___pyx_pf_7pygfunc_f
Martin
我使用gfortran并将编译命令直接写入安装文件。
gfunc.f90
模块gfunc_module
隐式无
包含
子程序gfunc(x,n,m,a ,b,c)
双精度,intent(in):: x
整数,intent(in):: n,m
双精度,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
模块gfunc1_interface
使用iso_c_binding,仅:c_double,c_int
使用gfunc_module,仅:gfunc
隐式无
包含
子例程c_gfunc(x,n,m,a,b ,c)绑定(c)
real(c_double),意图(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
调用gfunc(x,n,m, a,b,c)
结束子程序
结束模块
$ c $> $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' * m,double * a,double * b,double * c);
pygfunc.pyx
$ b
,从numpy cimport ndarray中清空
作为ar
cdef extern frompygfunc.h :
void c_gfunc(double * a,int * n,int * m,double * a,double * b,double * c)
def f(double x,double a =
cdef:
ar [double] ax = linspace(a,b,n)
ar [double,ndim = 2] c =空((n,n),order ='F')
c_gfunc(& x,& n,& n,< double *> ax.data,< double *> ax.data,< double *> c.data)
返回c
setup.py
from distutils.core import setup
from distutils.extension import Cython.Distutils的扩展
import build_ext
#这行只有在Cython文件中使用NumPy构建时才需要。从numpy导入
get_include $ b $从os导入系统
#编译fortran模块而不链接
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
系统(shared_obj_comp)
ext_modules = [扩展名(#模块名称:
'pygfunc',
#源文件:
['pygfunc.pyx'],
#其他编译参数为gcc
extra_compile_args = [' - fPIC','-O3'],
#其他文件链接到
extra_link_args = ['gfunc.o' ,'pygfunc.o'])]
设置(name ='pygfunc',
cmdclass = {'build_ext':build_ext},
#如果使用NumPy构建,
#This inc在编译时加入NumPy头文件
include_dirs = [get_include()],
ext_modules = ext_modules)
test.py
#脚本从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 ))
我所做的大部分修改都不是非常重要的。这里是重要的。
-
您正在混合双精度和单精度浮点数。 使用real(Fortran),float(Cython)和float32(NumPy),并使用双精度(Fortran),double(Cyton)和float64(NumPy)。尽量不要无意中混合它们。我假设你在我的例子中想要双打。
-
您应该将所有变量作为指针传递给Fortran。它在这方面与C调用约定不匹配。 Fortran中的iso_c_binding模块仅与C命名约定相匹配。将数组作为指针传递,并将它们的大小作为单独的值传递。可能还有其他方法可以做到这一点,但我不知道。
这个设置文件显示了在构建时可以在哪里添加一些更有用的额外参数。
编译时,运行 python setup.py build_ext - -inplace
。要验证它是否有效,请运行测试脚本。
以下是fortran90.org上显示的示例: mesh_exp
以前还有两个我放在一起的地方:ftridiag , fssor
我当然不是这方面的专家,但这些例子可能是一个很好的开始。
I would like to set up a workflow to reach fortran routines from Python using Cython on a Windows Machine
after some searching I found : http://www.fortran90.org/src/best-practices.html#interfacing-with-c and https://stackoverflow.com/tags/fortran-iso-c-binding/info
and some code pices:
Fortran side:
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 side:
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
and the setup file:
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 )
all the files ar in one directory
the fortran files compile ( using NAG Fortran Builder ) pygfunc compiles
but linking them throws a:
error LNK2019: unresolved external symbol _c_gfunc referenced in function ___pyx_pf_7pygfunc_f
and of course:
fatal error LNK1120: 1 unresolved externals
What am I missing ? or is this way to set up a workflow between Python and Fortran damned from the beginning ?
THX Martin
Here's a minimum working example. I used gfortran and wrote the compile commands directly into the setup file.
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))
Most of the changes I made aren't terribly fundamental. Here are the important ones.
You were mixing double precision and single precision floating point numbers. Don't do that. Use real (Fortran), float (Cython), and float32 (NumPy) together and use double precision (Fortran), double (Cyton), and float64 (NumPy) together. Try not to mix them unintentionally. I assumed you wanted doubles in my example.
You should pass all variables to Fortran as pointers. It does not match the C calling convention in that regard. The iso_c_binding module in Fortran only matches the C naming convention. Pass arrays as pointers with their size as a separate value. There may be other ways of doing this, but I don't know any.
I also added some stuff in the setup file to show where you can add some of the more useful extra arguments when building.
To compile, run python setup.py build_ext --inplace
. To verify that it works, run the test script.
Here is the example shown on fortran90.org: mesh_exp
Here are two more that I put together some time ago: ftridiag, fssor I'm certainly not an expert at this, but these examples may be a good place to start.
这篇关于Fortran - Cython工作流程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!