nedlrichards
nedlrichards

Reputation: 322

creating a python package with fortran using c_iso_binding and cython

Is it possible to compile a python package using a setup.py file that uses fortran code with the workflow described in https://www.fortran90.org/src/best-practices.html#interfacing-with-c?

In brief, the workflow described here is to:

  1. create a fortran wrapper function that defines c_iso_bindings for a fortran subroutine
  2. create a cython .pyx function that creates a function using the generated c code from step #1

Is it possible to compile the codes from both of these steps in a single setup.py file? The numpy distutils package seems to be best for compiling fortran source, while cython is compiled with its own distutils package.

The solution in this question added explicit fortran compiler calls to the setup.py file, and uses the cython distutils package. This seems both a little messy, and to introduce the possibility that different compilers are used to generate the code.

Upvotes: 3

Views: 433

Answers (1)

nedlrichards
nedlrichards

Reputation: 322

It seems like numpy has the functionality to compile fortran code and link to it as a library. Here is a minimal working example following gfunc in this question, but I want to leave the question if there is a better solution.

First, run cython to generate c code:

cython pygfunc.pyx -3

The setup.py file compiles the fortran code as a library, and links with the generated cython c code.

from numpy.distutils.misc_util import Configuration
from numpy.distutils.core import setup

def configuration(parent_package='',top_path=None):
    config = Configuration('pygfunc', parent_package, top_path)
    config.add_library('fort', sources=['gfunc.f90', 'pygfunc.f90'])
    config.add_extension('pygfunc', sources=['pygfunc.c'], libraries=['fort'])
    return config

if __name__ == '__main__':
    setup(configuration=configuration)

This can be tested with a inplace build:

python setup.py build_ext --inplace

After navigating to the local pygfunc folder, the following test script should complete without an exception.

from pygfunc import f
import numpy as np

a = np.linspace(-1, 1, 4) ** 2
A, B = np.meshgrid(a, a, copy=False)

assert(np.allclose(f(1., a=-1., b=1., n=4), np.exp(-(A + B))))

Upvotes: 2

Related Questions