pythonarraysstringfortranf2py

How to pass array of strings to Fortran subroutine using f2py


After some digging online and trial-error, I am still wondering how to passing arrays of strings from Python to Fortran by f2py.

I have the Fortran subroutine in string.f90 as:

  SUBROUTINE FOO(A)
  CHARACTER*5,dimension(10),intent(inout):: A
  PRINT*, "A=",A
  END

Then I run f2py -m mystring -c string.f90. The compiling was successful.

The python session is in test.py:

import mystring
import numpy as np
nobstot=10
xstring=np.empty(nobstot,dtype='S5')
xstring[0]="ABCDE"
mystring.foo(xstring)

Run python test.py, I have the error message:

1-th dimension must be 5 but got 0 (not defined).
Traceback (most recent call last) :
File "test.py", line 6, in <module>
mystring.foo(xstring)
mystring.error: failed in converting 1st argument `a' of mystring.foo to C/Fortran array

In the f2py compiling step, the gfortran and gcc compiler were invoked.

After >>> print mystring.foo.__doc__, there was:

foo(a)
Wrapper for ``foo``.
Parameters
---------
a : in/output rank-2 array('S') with bounds (10,5)

So, I tried test.py as:

import mystring
import numpy as np
nobstot=10
xstring=np.empty((nobstot,5),dtype='S1')
print xstring.shape
xstring[0]="ABCDE"
mystring.foo(xstring)

Then run python test.py, the error message was:

Traceback (most recent call last):
File "test.py", line 7, in <module>
mystring.foo(xstring)
ValueError: failed to initialize intent(inout) array -- input 'S' not compatible to 'c'

Solution

  • First, to pass array of strings to Fortran, in Python, you must create an array of chars with shape (<number of strings>, <string length>), fill its content, and then pass the char array to f2py generated function. Using your example:

    xstring = np.empty((nobstot, 5), dtype='c')
    xstring[0] = "ABCDE"
    xstring[1] = "FGHIJ"
    mystring.foo(xstring)
    

    In order this to work, you'll need to change your Fortran code too:

    subroutine foo(A)
    character*5, dimension(10), intent(in) :: A
    print*, "A(1)=",A(1)
    print*, "A(2)=",A(2)
    end
    

    Notice that intent(inout) is replaced with intent(in). This is because strings in Python, as well as strings in numpy arrays of strings, are immutable but in Fortran they may not be. As a result, the memory layout of Python strings cannot simply passed to Fortran functions and the user must reorganize the string data as explained above.

    Second, if your Fortran code changes the strings, as usage of intent(inout) suggests, you'll need to declare such string arguments as intent(in, out), for instance, by using f2py directive. Here follows a complete example:

    subroutine foo(A)
    character*5, dimension(10), intent(inout) :: A
    !f2py intent(in, out) A
    print*, "A(1)=",A(1)
    print*, "A(2)=",A(2)
    A(1)="QWERT"
    end
    

    F2py call:

    f2py -m mystring -c string.f90
    

    Python test script:

    import mystring
    import numpy as np
    nobstot = 10
    xstring = np.empty((nobstot, 5), dtype='c')
    xstring[0] = "ABCDE"
    xstring[1] = "FGHIJ"
    xstring = mystring.foo(xstring)
    print("xstring[0]=",string[0].tostring())
    print("xstring[1]=",string[1].tostring())
    

    Console output:

     A(1)=ABCDE
     A(2)=FGHIJ
    xstring[0]= QWERT
    xstring[1]= FGHIJ