High-Performance Python – Compiled Code and Fortran Interface

Soon after C became the first language to be callable from Python, people wanted the same for the millions of lines of proven Fortran code in scientific applications. In the past, combining C and Fortran was dependent on the compiler. The advent of Fortran 2003 brought a standard, compiler-independent C/Fortran interface.

The approach for combining C and Fortran for Python is to write a C wrapper for the Fortran functions and subroutines that you want to use in Python. Then, you build the Python function around the C wrapper and use it as a Python module. Calling the C function calls the Fortran routine. Although a little indirect, the concept is straightforward.

In the first example, I integrate Cython and Fortran.

Fortran/C and Cython

The Fortran 90 website, which lists best practices, discusses how elements of the Fortran 2003 standard can integrate C and Fortran. The iso_c_binding  module of Fortran 2003-compliant compilers matches Fortran and C types with named constants used as KIND -type parameters in Fortran.

Without writing any C code, Cython can then be used to interface with the Fortran code. The Cython code is written in Fortran with the use of C calling conventions. The Fortran code interface with C uses the iso_c_binding  module to link Cython directly against the Fortran library. Granted, this method doesn’t really combine C and Fortran; rather, it uses the standardized Fortran/C interface. However, it really is integrating Fortran and C with Python.

If you are interested, the Fortran 90 website has an example of interfacing Fortran with Python via Cython that is as simple as creating a C interface in Fortran. Another good example can be found on Pierre Augier’s website.

Fortran/C and ctypes

In the previous article on high-performance Python, I presented ctypes  as a way to integrate “foreign” function libraries (i.e., libraries outside of Python) into Python. You can use ctypes  to integrate Fortran code with Python, as well.

The following examples show the multiplication of two integers and the addition of two integers. Save the Fortran integer multiplication code function in file mult.f90 ,

integer function multiply(a, b)
    integer, intent(in) :: a, b
    multiply = a * b

and save the Fortran integer addition code in file add.f90 :

integer function addtwo(a, b)
    integer, intent(in) :: a, b
    addtwo = a + b
end function addtwo

You should compile each function individually into shared objects:

$ gfortran -shared -fPIC -g -o mult.so mult.f90
$ gfortran -shared -fPIC -g -o add.so add.f90

For the sake of development, I compile the code with the debug option (-g ), but that is purely a personal preference.

If you want to explore the shareable objects, you can use the Linux nm tool :

$ nm -ao mult.so | grep multiply
mult.so:00000000000005cc T multiply_

Note that the function name in the shareable object has a trailing underscore.

Now you can use the ctypes  module to check whether the objects work with code saved in a file named testfunc.py  (Listing 1). Notice that the variables a  and b  are typed by cytpes  and the Fortran functions are called with a trailing underscore. Also notice that variables are passed to the function by reference,which is different from C code, which passes by value.

Listing 1: Using ctypes

from ctypes import byref, cdll, c_int
 
mult = cdll.LoadLibrary('./mult.so')
add = cdll.LoadLibrary('./add.so')
a = c_int(2)
b = c_int(4)
print mult.multiply_(byref(a), byref(b))
print add.addtwo_(byref(a), byref(b))

The output from the Python code is:

$ python3 testfunc.py
8
6

If you like, you can write a simple Python wrapper function for the shareable objects, so it's easier to understand in the main Python code. A wrapper allows you to avoid the trailing underscore in the main Python code and the use of byref  functions to make it look more Pythonic. The previous article on high-performance Python has an example of how to write wrapper functions.

Related content

comments powered by Disqus