Search code examples
pythonnumpyf2py

f2py in numpy 2.0.1 does not expose variables the way numpy 1.26 did. How can I access Fortran variables in Python?


I used to run a collection of Fortran 95 subroutines from Python by compiling it via f2py. In the Fortran source I have a module with my global variables:

   MODULE GEOPLOT_GLOBALS
     IMPLICIT NONE
     INTEGER, PARAMETER :: N_MAX = 16
     INTEGER, PARAMETER :: I_MAX = 18
     INTEGER, PARAMETER :: J_MAX = 72
     ...
   END MODULE GEOPLOT_GLOBALS

The compiled file has the name "geoplot.cpython-312-darwin.so" and is in a subfolder named "geo". When using f2py in numpy 1.26, I could do this:

import geo.geoplot as geo
maxN = geo.geoplot_globals.n_max
maxI = geo.geoplot_globals.i_max
maxJ = geo.geoplot_globals.j_max

Now, with numpy 2.0.1, I do the same but get the error message

AttributeError: module 'geo.geoplot' has no attribute 'geoplot_globals'

Which can be confirmed by listing the __dict__ attribute or using the getmembers module: They all list the Fortran subroutines and modules which contain source code, except for the geoplot_globals module which contains only variable declarations.

So my question is: How am I supposed to access global Fortran variables from Python when using numpy 2.0? And please do not suggest to write all to a file in Fortran only to read it in Python. There should be a more direct way.


Solution

  • This must be a bug in f2py. See here: https://github.com/numpy/numpy/issues/27167

    What got me unstuck is an ugly workaround: I simply added some useless dummy code to the module, like that:

    MODULE GEOPLOT_GLOBALS
        USE MOD_TYPES
        IMPLICIT NONE
        INTEGER, PARAMETER :: N_MAX = 16
        INTEGER, PARAMETER :: I_MAX = 18
        INTEGER, PARAMETER :: J_MAX = 72
        ...
        CONTAINS
        SUBROUTINE DUMMY (UNSINN)
            INTEGER :: UNSINN
            OPEN(UNIT=29, FILE="FOR29.txt", STATUS = 'UNKNOWN')
            WRITE(29,"(I8)") UNSINN
            CLOSE (29)
        END SUBROUTINE DUMMY
    END MODULE GEOPLOT_GLOBALS
    

    Now the before missing module is carried over into Python and can be accessed in the usual way.

    >>> print(geo.geoplot_globals.__doc__)
    n_max : 'i'-scalar
    i_max : 'i'-scalar
    j_max : 'i'-scalar
    ...