Interfacing Fortran with Python in 2025

Hi,

I am building a project that mixes Fortran and python. My motivation is to build the scientific computing heavy parts using procedural subroutines in Fortran and interface it with the python which would be home to overall structure of the project.

I have previously tried to mixing different languages for example:

  • C++ with python using pybind11
  • C with python using the native cpython
  • C with fortran using iso_c_binding

However, I have limited sucess in mixing fortran with python (I’ve had many issues with f2py in the past). That said, what are some of the effective ways to mix modern Fortran with python?

PS: I really like pybind11, and using it with cmake and setuptools to build a python package. I am looking for similar reliable solution to build my project in Fortran.

2 Likes

A recent thread Calling Fortran from Python/Julia/Matlab discussed this.

Thanks for the suggestion. However, as mentioned in that thread. Most options seem to involve writing a lot of extra code that would then have to be maintained.

For example, in case of python/C++ interface with pybind11, there is only a minimal interface that has to be written to expose the C++ side to python and the main code isn’t polluted with the interface calls and functions.

1 Like

You would always need to maintain some amount of extra code. The question is, how much freedom do you want to keep or are willing to give away. I’ve been personally disappointed by the restrictions of automagical tooling, so at the end, since already a few years, I decided to go the ctypes way with Fortran+iso_c_binding, no need to write intermediate C code, you can check a micro example here Optimization Without Using Derivatives: the PRIMA Package, its Fortran Implementation, and Its Inclusion in SciPy - #19 by hkvzjal

1 Like

You could stick with pybind11 and just use gfortran to write the C headers for your bind(c) procedures (the ones implemented in Fortran):

To generate the header use:

$ gfortran -fc-prototypes -fsyntax-only fortranlib.f90 > fortranlib.h

Then you can just follow your standard C/C++ + pybind11 workflow.


I haven’t used pybind11 (or nanobind) before. Does a C function prototype contain all the needed information to write the pybind11 binding? If yes, there may be some ways to automate this. I’ve given a talk about something similar recently: FOSDEM 2025 - Easier API Interoperability: writing a bindings Generator to C/C++ with Coccinelle

1 Like

Regarding the example you shared and iso_c_binding, I really don’t like the way one has to change the everything to ctype. I don’t mind writing some wrapper code on one side and maintain it at the cost of keeping the main code clean or standalone program in native Fortran.

While I would like to compile it with ifx along with other llvm-based intel compilers, I don’t mind compiling it with gfortran. However, I am not sure why would be a compiler specific solution?

However, in retrospect I think it may not be a bad idea to wrap a Fortran shared object using cffi, as it may provide a more native interface (lower level) with python.

Other Fortran compilers don’t have the prototype generation flag. You don’t need to compile it with gfortran, you just use it to generate a C header.

You can think of it purely as a C prototype generation tool:

$ ./fortproto.sh          
Usage: fortproto.sh [-o output] file1.f90 [file2.f90 ...]
fortproto.sh

Generated using ChatGPT:

#!/bin/bash

output=""
files=()

while [[ $# -gt 0 ]]; do
    case "$1" in
        -o)
            output="$2"
            shift 2
            ;;
        *)
            files+=("$1")
            shift
            ;;
    esac
done

if [[ ${#files[@]} -eq 0 ]]; then
    echo "Usage: $(basename "$0") [-o output] file1.f90 [file2.f90 ...]" >&2
    exit 1
fi

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

for file in "${files[@]}"; do
    gfortran -fc-prototypes -fsyntax-only "$file" >> "$tmpfile"
done

if [[ -n "$output" ]]; then
    mv "$tmpfile" "$output"
else
    cat "$tmpfile"
fi
2 Likes

Would you mind explaining a bit more what do you mean by “change everything”? From my experience, ctypes and cffi work in a similar way by exposing the low-level types between the Python interpreter and your C-compatible library. So you need to specify your in/out variables for your own functions that are interoperating within the wrapper module only.

2 Likes

I was of the opinion that writing pure Fortran and wrapping it with cffi would be better than introducing typecasting object at the level of Fortran using iso_c_binding. If I understand correctly, the main issue here would be to careful pass arrays/data between python and Fortran. I believe this could be done with cffi at a higher level in python, to make sure array order complies with fortran.

1 Like

iso_c_binding is the module which provides the interoperable types/kind specifiers. The key is the bind(c) attribute which declares the use of C linkage conventions.

In principle, if you know the calling convention of your Fortran compiler (and these differ among compilers!) you could construct the right function with CFFI. But you’d need a tool (i.e. f2py or something similar) that can parse Fortran interface blocks (or procedures) and generate that information.

The C interop route has the advantage that some of this “infrastructure” exists already: a) the -fc-prototypes flag in gfortran, and b) the libraries ctypes, cffi, pybind11, … on the C side. But you are right, it does potentially require writing a little extra code in Fortran (the bind(c) procedures), and the C/Python glue.


This can be done with ctypes too, assuming you’ll be interfacing with NumPy arrays, see e.g. ctypes foreign function interface (numpy.ctypeslib) — NumPy v2.2 Manual. You would use this in your list of argument types:

flib.somefunc.argtypes = [np.ctypeslib.ndpointer(dtype=np.float64,
                                                 ndim=2,
                                                 flags='F_CONTIGUOUS'), ...]

I used this way as well and found it to be the least problematic.

If your code is in Fortran modules then there’s my package GitHub - rjfarmer/gfort2py: Library to allow calling fortran code from python where you don’t have to write any interface code your self and as long as you make a shared library at the end of your build then no build changes are needed (you just point your python code to the shared library and a Fortran module).

It’s using ctypes under the hood but reads gfortran’s mod files to work out the interface for you, so no c/c++ code needed. It also doesn’t need to be using iso_c_binding or even bind(c).

1 Like

hello,

I am developing such a code for numerical simulation. I use fortran, iso_c_binding and cython. It works nicely and I can even create classes with fortran by creating a container with all my types and transmit it through python interface as a type(c_ptr) i.e void*

1 Like

Thanks for the suggestion. I am curious to know how you build your package? I’ve been stuck to using CMake for most of my builds with python and C++. It also works well for stand-alone Fortran packages. However, I am not sure how easy or hard it woul be to interface with setuptools with such an interface on both sides.

I had some success in the past using scikit-build in conjunction with CMake, and f2py for python bindings to our Fortran code. I see that it is superseded by scikit-build-core

1 Like

@scottza Thanks for the suggestion, using scikit-build-core was great idea.

After trying out some of the options suggested here and little exploration, I ended up finding f90wrap, which work like a breeze. It builds on top of the exisiting f2py code and supports derived types.

They also have a short article here that summarizes their approach: J. Phys.: Condens. Matter 32 305901

@ivanpribec if you haven’t already checked this out, I am guessing this might be something of interest to you.

7 Likes

A new project is

I don’t create Python interfaces for my codes, but people who do could try using this project and say if it makes using ctypes easier. I list projects related to interoperating Fortran with other languages here.