f2py: Running Fortran code in Python on Windows, Linux, and Mac

f2py allows importing unmodified Fortran code as a library/module. If the Fortran does not specify “intents”, you can add !f2py intent() comments instead of actual code.

Here is a simple example of correctly specifying Fortran Intent() vis-à-vis Python f2py that you should consider before starting your Fortran->Python project.

Install f2py

Linux

(also for Windows Subsystem for Linux)

apt install gfortran
conda install numpy

Mac OS

brew install gcc
conda install numpy

Windows

Alternative: Windows Subsystem for Linux.

Tested on a 64-bit Windows 10 machine.

  1. install Numpy

     conda install numpy
    
  2. install MinGW-W64 to c:\mingw with the setup settings:

    Architecture Threads Exception
    x86_64 posix seh
  3. Insert the mingw bin path to your system path permanently: Control Panel > Advanced System Settings > Environment Variables > System variables > Path. At the end put the path to gfortran.exe, such as:

     C:\mingw\mingw64\bin
    
  4. Tell Python where your compiler is at by creating the file c:\Anaconda\Lib\distutils\distutils.cfg with the content

     [build]
     compiler=mingw32
    

Test/fix your f2py installation

Try the lowtran7 code. Following the instructions there, you should get

  1. A lowtran7.pyd (on Windows) or lowtran7*.so (on Linux) file
  2. Running python DemoLowtran.py creates a plot of atmospheric loss
Lowtran atmospheric loss line plot
Lowtran atmospheric loss.

f2py does not allow inline comments for COMMON blocks

f2py through at least Numpy 1.12 does not allow inline comments for COMMON blocks. f2py works a little more strictly to Fortran specifications than most modern compilers. This error means you have a subroutine or function call with an inline comment in a Fortran77 file. Inline comments are not Fortran 77 standard, and will make f2py error. To fix, just make the inline comment a full-line command with ! in column 1. A Fortran90 .f90 file won’t have an error due to inline comments on a line with a COMMON block.

This will manifest itself two different ways, depending on whether you have implicit none or not:

COMMON inline comment error WITH implicit none

Example in badcomment_implicit.f

var2fixfortran: No typespec for argument “x ! bad for fortran77”. getctype: No C-type found in “{}”, assuming void. KeyError: ‘void’

Solution: Make inline comment a full-line comment with ! in column 1.

COMMON inline comment error WITHOUT implicit none

Example in badcomment.f

error: expected ‘;’, ‘,’ or ‘)’ before ‘!’ token

Solution: Make inline comment a full-line comment with ! in column 1.

Troubleshooting f2py in Windows

Another solution is to use Windows Subsystem for Linux with Anaconda Python or Cygwin. With a combination of the techniques below, I’ve always gotten f2py to work on Windows 7 and Windows 10.

‘f2py’ is not recognized as an internal or external command, operable program or batch file.

  1. Ensure Numpy is installed

     conda install numpy
    
  2. (Windows) create a file <Anaconda Python install directory>/Scripts/f2py.bat with content

     python %~dp0/f2py.py %*
    

Error: No module named ‘numpy.distutils._msvccompiler’ in numpy.distutils; trying from distutils

is fixed by step #4 above.

NotImplementedError: Only MS compiler supported with gfortran on win64

manually patch the file <Anaconda Python install directory>/Lib/site-packages/numpy/distutils/fcompiler/gnu.py on line 337. replace line 337

#raise NotImplementedError(...)

with the word

pass

Error: ValueError: Unknown MS Compiler version 1900`

  1. manually patch the file <Anaconda Python install directory>/Lib/distutils/cygwinccompiler.py on line 157 and line 318, replacing

     #self.dll_libraries = get_msvcr()
    

    with the word

     pass
    
  2. manually patch the file <Anaconda Python install directory>/Lib/site-packages/numpy/distutils/mingw32compiler.py, commenting out lines 96-104

     # Check for custom msvc runtime library on Windows. Build if it doesn't exist.
     #msvcr_success = build_msvcr_library()
     #msvcr_dbg_success = build_msvcr_library(debug=True)
     #if msvcr_success or msvcr_dbg_success:
         # add preprocessor statement for using customized msvcr lib
     #    self.define_macro('NPY_MINGW_USE_CUSTOM_MSVCR')
    
     # Define the MSVC version as hint for MinGW
     #msvcr_version = '0x%03i0' % int(msvc_runtime_library().lstrip('msvcr'))
     #self.define_macro('__MSVCRT_VERSION__', msvcr_version)
    

References

Reference