Фортран - Рабочий процесс Cython

Я хотел бы настроить рабочий процесс для доступа к подпрограммам fortran из Python с использованием Cython на машине Windows

после некоторого поиска я нашел: http://www.fortran90.org/src/best-practices.html#interfacing-with-c и https://stackoverflow.com/tags/fortran-iso-c-binding/info

и некоторые цены кода:

Сторона Фортрана:

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

и конечно:

фатальная ошибка LNK1120: 1 неразрешенные внешние элементы

Что мне не хватает? или этот способ наладить рабочий процесс между Python и Fortran проклят с самого начала?

THX Martin


person martburg    schedule 14.03.2014    source источник
comment
Это странно. Разве вы не вызываете c_gfunc где-нибудь из Фортрана без явного интерфейса или каким-либо другим способом как процедуру Фортрана, а не процедуру Си?   -  person Vladimir F    schedule 14.03.2014
comment
Извини, я не понял вопроса   -  person martburg    schedule 14.03.2014
comment
не решая вашу проблему, но если вы не знали, есть также альтернатива, которую вы можете рассмотреть: f2py.   -  person steabert    schedule 14.03.2014
comment
Ничего, это ни к чему не приведет. Очевидно, Cython вообще не использует ваш объектный файл Fortran. Я недостаточно знаю Cython, чтобы помочь вам. Я смог запустить ваш код, используя ctypes.   -  person Vladimir F    schedule 14.03.2014
comment
@steabert Тоже не решает, но я думаю, что ctypes - это подход, который намного ближе к Cython. Код на Фортране может остаться как есть.   -  person Vladimir F    schedule 14.03.2014


Ответы (1)


Вот минимальный рабочий пример. Я использовал 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))

Большинство изменений, которые я сделал, не являются фундаментальными. Вот самые важные.

  • Вы смешивали числа с плавающей запятой двойной точности и одинарной точности. Не делайте этого. Используйте вместе real (Fortran), float (Cython) и float32 (NumPy) и одновременно используйте двойную точность (Fortran), double (Cyton) и float64 (NumPy). Постарайтесь не смешивать их непреднамеренно. Я предположил, что в моем примере вам нужны двойники.

  • Вы должны передать все переменные в Фортран как указатели. В этом отношении это не соответствует соглашению о вызовах C. Модуль iso_c_binding в Фортране соответствует только соглашению об именах C. Массивы передаются как указатели, а их размер - как отдельное значение. Могут быть и другие способы сделать это, но я не знаю ни одного.

Я также добавил кое-что в установочный файл, чтобы показать, где вы можете добавить некоторые из наиболее полезных дополнительных аргументов при сборке.

Для компиляции запустите python setup.py build_ext --inplace. Чтобы убедиться, что он работает, запустите тестовый сценарий.

Вот пример, показанный на fortran90.org: mesh_exp

Вот еще два, которые я собрал некоторое время назад: ftridiag, fssor Я, конечно, не эксперт в этом, но эти примеры могут быть хорошей отправной точкой.

person IanH    schedule 20.03.2014
comment
THX, явный setup.py был откровением - person martburg; 20.03.2014
comment
У меня есть сообщение Cython, где вы может дать представление о. - person Phillip; 31.01.2017