Fortran - Cython工作流程 [英] Fortran - Cython Workflow

查看:166
本文介绍了Fortran - Cython工作流程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想建立一个工作流程,在Windows机器上使用Cython从Python到达Fortran例程



经过一番搜索后,我发现:
http://www.fortran90.org/src/best-practices。 html#与c ++接口 https://stackoverflow.com/tags/fortran-

和一些代码片段:

Fortran方面:

/ p>

pygfunc.h:

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

pygfunc.f90

 模块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.f90

 模块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

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,(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)

所有文件在一个目录中



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



但链接它们会抛出:

错误LNK2019:无法解析的外部符号_c_gfunc引用
函数___pyx_pf_7pygfunc_f

p>

以及当然:

致命错误LNK1120:1个未解析的外部元素

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

THX
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

$从numpy导入linspace获得b
$ 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆