开发者

Cython, C and Fortran

开发者 https://www.devze.com 2023-01-29 15:17 出处:网络
I would like to ask your help about calling fortran fun开发者_如何学Pythonction through C functions. These C functions will be used in python code through cython. Putting things together, I have this

I would like to ask your help about calling fortran fun开发者_如何学Pythonction through C functions. These C functions will be used in python code through cython. Putting things together, I have this scheme:

Cython Module -> C function -> Fortran, where -> means "calls".

Currently I managed to call the C function from cython, but I am having an hard time calling the fortran function. Can you help me?(an simple example would be great).

Thanks in advance. Edit: I am using gcc 4.1.2. And gfortran


The link in the first answer describes outdated methods. It has become much easier to call Fortran from C, or C from Fortran with the addition of the ISO C Binding to Fortran. Using this Fortran language feature instructs the Fortran compiler to emit executable code that is binary compatible with C. The programmer doesn't have to "hack" the connection and since it is part of the language is it compiler and platform independent. Technically the ISO C Binding is part of Fortran 2003 but it has been available for several years in numerous compilers, e.g., gfortran since version 4.3 and Intel ifort.

To call a Fortran subroutine or function from C, you declare the Fortran subroutine or function with the bind C option and use the C-compatible types provide in the binding for the declarations of the arguments. There are examples in the gfortran manual under "Mixed Language Programming". Since the ISO C Binding is a part of the language, this section of the manual is largely compiler independent. There are other examples in previous answers here on Stack Overflow and elsewhere on the web.

Here is a quick code fragment (untested) of a Fortran subroutine declaration of a subroutine to be called from C:

subroutine test ( varint1, varflt2 )  bind ( C, name="MyTest" )

   use iso_c_binding

   integer (kind=c_int32_t), intent (in) :: varint1
   real (kind=c_float), intent (out) :: varflt2

The bind C name "MyTest" overrides the Fortran name -- it is case sensitive, unlike Fortran. No need to worry about underscores! The variable types should be obvious ... see the gfortran manual or elsewhere for whats available.


Since this is a useful method for debugging HPC codes, here is a simple "hello world" program in Fortran, called from Python.

Using GNU Fortran (GCC) 9.3.0 and Python 3.7.3

You need, at a minimum

  1. A fortran code, with a c-binding (i.e. bind(C) for each subroutine/function, and iso_c_binding for each variable written in/out). Let's call this hello_fortran.f90
subroutine hello_fortran() bind(c)

    print *, "Hello world from fortran"

end subroutine hello_fortran

If you'd prefer not to modify your original Fortran code with iso_c_bindings, you can also write a simple wrapper function in Fortran that directly calls the original code.

Compile this code using the -c to compile without linking, and -fPIC to make position-independent code. gfortran hello_fortran.f90 -c -fPIC -o hello_fortran.o You can directly link the object files in your setup.py, but I find it much easier to bundle everything into a shared library gfortran *.o -shared -o libhello_fortran.so and add the current working directory to PATH with export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd)

2 . A Cython module which allows C functions to be called from Python (alternatively, you could use CFFI -- the method is similar). For our example, let's call this "hello_cython.pyx"

cdef extern:
    void hello_fortran()

def hello_cython():
    print("Called hello_cython")
    hello_fortran()

Crucially, for each Fortran function that you want to call, you need to declare the C-interface in the cdef extern block. Remember that fortran is case-insensitive: everything in this block should be lowercase.

  1. A setup.py file to "compile" the Cython module. Unfortunately, these can get very complicated very quickly, so what is given below is a minimum working example
from distutils.core import setup, Extension
from Cython.Build import cythonize
import numpy

files = ['hello_cython.pyx']

ext_module = Extension(
    name = "hello_cython_FI",
    sources = files,
    include_dirs = ['.'],
    library_dirs=['.'],
    libraries=["hello_fortran"]
    )

setup(
    name = "hello_cython_FI",
    ext_modules = cythonize(ext_module)
)

The important things to note are that the name will give the name of the module in Python, files must include the .pyx file, and libraries must have the name of the shared library (i.e. for libhello_fortran.so, write hello_fortran)

Compile this with python setup.py build_ext --inplace

  1. A standard Python script hello_python.py which calls hello_cython_FI
import hello_cython_FI

print("Called hello_python")
hello_cython_FI.hello_cython()

Then you should get

>>> python hello_python.py
Called hello_python
Called hello_cython
Hello world from fortran

Ask in comments for Intel, OpenMP and OpenMP linking methods.


There is an automated tool called fwrap which generates, C, cython and python bindings to fortran routines. I think it still has beta status but you might find it helpful, the link is here.

0

精彩评论

暂无评论...
验证码 换一张
取 消