Using F2PY bindings in Python#

In this page, you can find a full description and a few examples of common usagepatterns for F2PY with Python and different argument types. For more examplesand use cases, seeF2PY examples.

Fortran type objects#

All wrappers for Fortran/C routines, common blocks, or Fortran 90 module datagenerated by F2PY are exposed to Python asfortran type objects. Routinewrappers are callablefortran type objects while wrappers to Fortran datahave attributes referring to data objects.

Allfortran type objects have an attribute_cpointer that contains aPyCapsule referring to the C pointer of the corresponding Fortran/C functionor variable at the C level. SuchPyCapsule objects can be used as callbackarguments for F2PY generated functions to bypass the Python C/API layer forcalling Python functions from Fortran or C. This can be useful when thecomputational aspects of such functions are implemented in C or Fortran andwrapped with F2PY (or any other tool capable of providing thePyCapsulecontaining a function).

Consider a Fortran 77 file`ftype.f:

CFILE:FTYPE.FSUBROUTINEFOO(N)INTEGERNCf2pyintegeroptional,intent(in)::n=13REALA,XCOMMON/DATA/A,X(3)CPRINT*,"IN FOO: N=",N," A=",A," X=[",X(1),X(2),X(3),"]"ENDCENDOFFTYPE.F

and a wrapper built usingf2py-cftype.f-mftype.

In Python, you can observe the types offoo anddata, and how to accessindividual objects of the wrapped Fortran code.

>>>importftype>>>print(ftype.__doc__)This module 'ftype' is auto-generated with f2py (version:2).Functions:  foo(n=13)COMMON blocks:  /data/ a,x(3).>>>type(ftype.foo),type(ftype.data)(<class 'fortran'>, <class 'fortran'>)>>>ftype.foo() IN FOO: N= 13 A=  0. X=[  0.  0.  0.]>>>ftype.data.a=3>>>ftype.data.x=[1,2,3]>>>ftype.foo() IN FOO: N= 13 A=  3. X=[  1.  2.  3.]>>>ftype.data.x[1]=45>>>ftype.foo(24) IN FOO: N= 24 A=  3. X=[  1.  45.  3.]>>>ftype.data.xarray([  1.,  45.,   3.], dtype=float32)

Scalar arguments#

In general, a scalar argument for a F2PY generated wrapper function can be anordinary Python scalar (integer, float, complex number) as well as an arbitrarysequence object (list, tuple, array, string) of scalars. In the latter case, thefirst element of the sequence object is passed to the Fortran routine as ascalar argument.

Note

  • When type-casting is required and there is possible loss of information vianarrowing e.g. when type-casting float to integer or complex to float, F2PYdoes not raise an exception.

    • For complex to real type-casting only the real part of a complex numberis used.

  • intent(inout) scalar arguments are assumed to be array objects inorder to havein situ changes be effective. It is recommended to usearrays with proper type but also other types work.Read more aboutthe intent attribute.

Consider the following Fortran 77 code:

CFILE:SCALAR.FSUBROUTINEFOO(A,B)REAL*8A,BCf2pyintent(in)aCf2pyintent(inout)bPRINT*,"    A=",A," B=",BPRINT*,"INCREMENT A AND B"A=A+1D0B=B+1D0PRINT*,"NEW A=",A," B=",BENDCENDOFFILESCALAR.F

and wrap it usingf2py-c-mscalarscalar.f.

In Python:

>>>importscalar>>>print(scalar.foo.__doc__)foo(a,b)Wrapper for ``foo``.Parameters----------a : input floatb : in/output rank-0 array(float,'d')>>>scalar.foo(2,3)     A=  2. B=  3. INCREMENT A AND B NEW A=  3. B=  4.>>>importnumpy>>>a=numpy.array(2)# these are integer rank-0 arrays>>>b=numpy.array(3)>>>scalar.foo(a,b)     A=  2. B=  3. INCREMENT A AND B NEW A=  3. B=  4.>>>print(a,b)# note that only b is changed in situ2 4

String arguments#

F2PY generated wrapper functions accept almost any Python object as a stringargument, sincestr is applied for non-string objects. Exceptions are NumPyarrays that must have type code'S1' or'b' (corresponding to theoutdated'c' or'1' typecodes, respectively) when used as stringarguments. SeeScalars for more information on these typecodes.

A string can have an arbitrary length when used as a string argument for an F2PYgenerated wrapper function. If the length is greater than expected, the stringis truncated silently. If the length is smaller than expected, additional memoryis allocated and filled with\0.

Because Python strings are immutable, anintent(inout) argument expects anarray version of a string in order to havein situ changes be effective.

Consider the following Fortran 77 code:

CFILE:STRING.FSUBROUTINEFOO(A,B,C,D)CHARACTER*5A,BCHARACTER*(*)C,DCf2pyintent(in)a,cCf2pyintent(inout)b,dPRINT*,"A=",APRINT*,"B=",BPRINT*,"C=",CPRINT*,"D=",DPRINT*,"CHANGE A,B,C,D"A(1:1)='A'B(1:1)='B'C(1:1)='C'D(1:1)='D'PRINT*,"A=",APRINT*,"B=",BPRINT*,"C=",CPRINT*,"D=",DENDCENDOFFILESTRING.F

and wrap it usingf2py-c-mmystringstring.f.

Python session:

>>>importmystring>>>print(mystring.foo.__doc__)foo(a,b,c,d)Wrapper for ``foo``.Parameters----------a : input string(len=5)b : in/output rank-0 array(string(len=5),'c')c : input string(len=-1)d : in/output rank-0 array(string(len=-1),'c')>>>fromnumpyimportarray>>>a=array(b'123\0\0')>>>b=array(b'123\0\0')>>>c=array(b'123')>>>d=array(b'123')>>>mystring.foo(a,b,c,d) A=123 B=123 C=123 D=123 CHANGE A,B,C,D A=A23 B=B23 C=C23 D=D23>>>a[()],b[()],c[()],d[()](b'123', b'B23', b'123', b'D2')

Array arguments#

In general, array arguments for F2PY generated wrapper functions acceptarbitrary sequences that can be transformed to NumPy array objects. There aretwo notable exceptions:

  • intent(inout) array arguments must always beproper-contiguous and have a compatibledtype,otherwise an exception is raised.

  • intent(inplace) array arguments will be changedin situ if the argumenthas a different type than expected (see theintent(inplace)attribute for more information).

In general, if a NumPy array isproper-contiguous and hasa proper type then it is directly passed to the wrapped Fortran/C function.Otherwise, an element-wise copy of the input array is made and the copy, beingproper-contiguous and with proper type, is used as the array argument.

Usually there is no need to worry about how the arrays are stored in memory andwhether the wrapped functions, being either Fortran or C functions, assume oneor another storage order. F2PY automatically ensures that wrapped functions getarguments with the proper storage order; the underlying algorithm is designed tomake copies of arrays only when absolutely necessary. However, when dealing withvery large multidimensional input arrays with sizes close to the size of thephysical memory in your computer, then care must be taken to ensure the usage ofproper-contiguous and proper type arguments.

To transform input arrays to column major storage order before passingthem to Fortran routines, use the functionnumpy.asfortranarray.

Consider the following Fortran 77 code:

CFILE:ARRAY.FSUBROUTINEFOO(A,N,M)CCINCREMENTTHEFIRSTROWANDDECREMENTTHEFIRSTCOLUMNOFACINTEGERN,M,I,JREAL*8A(N,M)Cf2pyintent(in,out,copy)aCf2pyintegerintent(hide),depend(a)::n=shape(a,0),m=shape(a,1)DOJ=1,MA(1,J)=A(1,J)+1D0ENDDO      DOI=1,NA(I,1)=A(I,1)-1D0ENDDO      ENDCENDOFFILE ARRAY.F

and wrap it usingf2py-c-marrarray.f-DF2PY_REPORT_ON_ARRAY_COPY=1.

In Python:

>>>importarr>>>fromnumpyimportasfortranarray>>>print(arr.foo.__doc__)a = foo(a,[overwrite_a])Wrapper for ``foo``.Parameters----------a : input rank-2 array('d') with bounds (n,m)Other Parameters----------------overwrite_a : input int, optional    Default: 0Returns-------a : rank-2 array('d') with bounds (n,m)>>>a=arr.foo([[1,2,3],...[4,5,6]])created an array from object>>>print(a)[[ 1.  3.  4.] [ 3.  5.  6.]]>>>a.flags.c_contiguousFalse>>>a.flags.f_contiguousTrue# even if a is proper-contiguous and has proper type,# a copy is made forced by intent(copy) attribute# to preserve its original contents>>>b=arr.foo(a)copied an array: size=6, elsize=8>>>print(a)[[ 1.  3.  4.] [ 3.  5.  6.]]>>>print(b)[[ 1.  4.  5.] [ 2.  5.  6.]]>>>b=arr.foo(a,overwrite_a=1)# a is passed directly to Fortran...# routine and its contents is discarded...>>>print(a)[[ 1.  4.  5.] [ 2.  5.  6.]]>>>print(b)[[ 1.  4.  5.] [ 2.  5.  6.]]>>>aisb# a and b are actually the same objectsTrue>>>print(arr.foo([1,2,3]))# different rank arrays are allowedcreated an array from object[ 1.  1.  2.]>>>print(arr.foo([[[1],[2],[3]]]))created an array from object[[[ 1.]  [ 1.]  [ 2.]]]>>>>>># Creating arrays with column major data storage order: ...>>>s=asfortranarray([[1,2,3],[4,5,6]])>>>s.flags.f_contiguousTrue>>>print(s)[[1 2 3] [4 5 6]]>>>print(arr.foo(s))>>>s2=asfortranarray(s)>>>s2iss# an array with column major storage order               # is returned immediatelyTrue>>># Note that arr.foo returns a column major data storage order array: ...>>>s3=ascontiguousarray(s)>>>s3.flags.f_contiguousFalse>>>s3.flags.c_contiguousTrue>>>s3=arr.foo(s3)copied an array: size=6, elsize=8>>>s3.flags.f_contiguousTrue>>>s3.flags.c_contiguousFalse

Call-back arguments#

F2PY supports calling Python functions from Fortran or C codes.

Consider the following Fortran 77 code:

CFILE:CALLBACK.FSUBROUTINEFOO(FUN,R)EXTERNALFUNINTEGERIREAL*8R,FUNCf2pyintent(out)rR=0D0DOI=-5,5R=R+FUN(I)ENDDO      ENDCENDOFFILECALLBACK.F

and wrap it usingf2py-c-mcallbackcallback.f.

In Python:

>>>importcallback>>>print(callback.foo.__doc__)r = foo(fun,[fun_extra_args])Wrapper for ``foo``.Parameters----------fun : call-back functionOther Parameters----------------fun_extra_args : input tuple, optional    Default: ()Returns-------r : floatNotes-----Call-back functions::  def fun(i): return r  Required arguments:    i : input int  Return objects:    r : float>>>deff(i):returni*i...>>>print(callback.foo(f))110.0>>>print(callback.foo(lambdai:1))11.0

In the above example F2PY was able to guess accurately the signatureof the call-back function. However, sometimes F2PY cannot establish theappropriate signature; in these cases the signature of the call-backfunction must be explicitly defined in the signature file.

To facilitate this, signature files may contain special modules (the names ofthese modules contain the special__user__ sub-string) that define thevarious signatures for call-back functions. Callback arguments in routinesignatures have theexternal attribute (see also theintent(callback)attribute). To relate a callback argument with itssignature in a__user__ module block, ause statement can be utilized asillustrated below. The same signature for a callback argument can be referred toin different routine signatures.

We use the same Fortran 77 code as in the previous example but nowwe will pretend that F2PY was not able to guess the signatures ofcall-back arguments correctly. First, we create an initial signaturefilecallback2.pyf using F2PY:

f2py-mcallback2-hcallback2.pyfcallback.f

Then modify it as follows

!    -*- f90 -*-python module __user__routines     interface        function fun(i) result (r)            integer :: i            real*8 :: r        end function fun    end interfaceend python module __user__routinespython module callback2    interface        subroutine foo(f,r)            use __user__routines, f=>fun            external f            real*8 intent(out) :: r        end subroutine foo    end interface end python module callback2

Finally, we build the extension module usingf2py-ccallback2.pyfcallback.f.

An example Python session for this snippet would be identical to the previousexample except that the argument names would differ.

Sometimes a Fortran package may require that users provide routines that thepackage will use. F2PY can construct an interface to such routines so thatPython functions can be called from Fortran.

Consider the following Fortran 77 subroutine that takes an array as its inputand applies a functionfunc to its elements.

subroutinecalculate(x,n)cf2pyintent(callback)funcexternalfunccThefollowinglinesdefinethesignatureoffuncforF2PY:cf2pyreal*8ycf2pyy=func(y)ccf2pyintent(in,out,copy)xintegern,ireal*8x(n),funcdoi=1,nx(i)=func(x(i))end do      end

The Fortran code expects that the functionfunc has been defined externally.In order to use a Python function forfunc, it must have an attributeintent(callback) and it must be specified before theexternal statement.

Finally, build an extension module usingf2py-c-mfoocalculate.f

In Python:

>>>importfoo>>>foo.calculate(range(5),lambdax:x*x)array([  0.,   1.,   4.,   9.,  16.])>>>importmath>>>foo.calculate(range(5),math.exp)array([  1.        ,   2.71828183,   7.3890561,  20.08553692,  54.59815003])

The function is included as an argument to the python function call to theFortran subroutine even though it wasnot in the Fortran subroutine argumentlist. The “external” keyword refers to the C function generated by f2py, not thePython function itself. The python function is essentially being supplied to theC function.

The callback function may also be explicitly set in the module. Then it is notnecessary to pass the function in the argument list to the Fortran function.This may be desired if the Fortran function calling the Python callback functionis itself called by another Fortran function.

Consider the following Fortran 77 subroutine:

subroutinef1()print*,"in f1, calling f2 twice.."callf2()callf2()return      end      subroutinef2()cf2pyintent(callback,hide)fpyexternalfpyprint*,"in f2, calling f2py.."callfpy()return      end

and wrap it usingf2py-c-mpfromfextcallback.f.

In Python:

>>>importpfromf>>>pfromf.f2()Traceback (most recent call last):  File"<stdin>", line1, in<module>pfromf.error:Callback fpy not defined (as an argument or module pfromf attribute).>>>deff():print("python f")...>>>pfromf.fpy=f>>>pfromf.f2() in f2, calling f2py..python f>>>pfromf.f1() in f1, calling f2 twice.. in f2, calling f2py..python f in f2, calling f2py..python f>>>

Note

When using modified Fortran code viacallstatement or other directives,the wrapped Python function must be called as a callback, otherwise only thebare Fortran routine will be used. For more details, seenumpy/numpy#26681

Resolving arguments to call-back functions#

F2PY generated interfaces are very flexible with respect to call-back arguments. For each call-back argument an additional optionalargument<name>_extra_args is introduced by F2PY. This argument can be usedto pass extra arguments to user provided call-back functions.

If a F2PY generated wrapper function expects the following call-back argument:

deffun(a_1,...,a_n):...returnx_1,...,x_k

but the following Python function

defgun(b_1,...,b_m):...returny_1,...,y_l

is provided by a user, and in addition,

fun_extra_args=(e_1,...,e_p)

is used, then the following rules are applied when a Fortran or C functionevaluates the call-back argumentgun:

  • Ifp==0 thengun(a_1,...,a_q) is called, hereq=min(m,n).

  • Ifn+p<=m thengun(a_1,...,a_n,e_1,...,e_p) is called.

  • Ifp<=m<n+p thengun(a_1,...,a_q,e_1,...,e_p) is called,and hereq=m-p.

  • Ifp>m thengun(e_1,...,e_m) is called.

  • Ifn+p is less than the number of required arguments togun then anexception is raised.

If the functiongun may return any number of objects as a tuple; then thefollowing rules are applied:

  • Ifk<l, theny_{k+1},...,y_l are ignored.

  • Ifk>l, then onlyx_1,...,x_l are set.

Common blocks#

F2PY generates wrappers tocommon blocks defined in a routine signatureblock. Common blocks are visible to all Fortran codes linked to the currentextension module, but not to other extension modules (this restriction is due tothe way Python imports shared libraries). In Python, the F2PY wrappers tocommon blocks arefortran type objects that have (dynamic) attributesrelated to the data members of the common blocks. When accessed, theseattributes return as NumPy array objects (multidimensional arrays areFortran-contiguous) which directly link to data members in common blocks. Datamembers can be changed by direct assignment or by in-place changes to thecorresponding array objects.

Consider the following Fortran 77 code:

CFILE:COMMON.FSUBROUTINEFOOINTEGERI,XREALACOMMON/DATA/I,X(4),A(2,3)PRINT*,"I=",IPRINT*,"X=[",X,"]"PRINT*,"A=["PRINT*,"[",A(1,1),",",A(1,2),",",A(1,3),"]"PRINT*,"[",A(2,1),",",A(2,2),",",A(2,3),"]"PRINT*,"]"ENDCENDOFCOMMON.F

and wrap it usingf2py-c-mcommoncommon.f.

In Python:

>>>importcommon>>>print(common.data.__doc__)i : 'i'-scalarx : 'i'-array(4)a : 'f'-array(2,3)>>>common.data.i=5>>>common.data.x[1]=2>>>common.data.a=[[1,2,3],[4,5,6]]>>>common.foo()>>>common.foo() I=           5 X=[           0           2           0           0 ] A=[ [   1.00000000     ,   2.00000000     ,   3.00000000     ] [   4.00000000     ,   5.00000000     ,   6.00000000     ] ]>>>common.data.a[1]=45>>>common.foo() I=           5 X=[           0           2           0           0 ] A=[ [   1.00000000     ,   2.00000000     ,   3.00000000     ] [   45.0000000     ,   45.0000000     ,   45.0000000     ] ]>>>common.data.a# a is Fortran-contiguousarray([[  1.,   2.,   3.],       [ 45.,  45.,  45.]], dtype=float32)>>>common.data.a.flags.f_contiguousTrue

Fortran 90 module data#

The F2PY interface to Fortran 90 module data is similar to the handling ofFortran 77 common blocks.

Consider the following Fortran 90 code:

modulemodintegeriinteger::x(4)real,dimension(2,3)::areal,allocatable,dimension(:,:)::bcontains  subroutinefoointegerkprint*,"i=",iprint*,"x=[",x,"]"print*,"a=["print*,"[",a(1,1),",",a(1,2),",",a(1,3),"]"print*,"[",a(2,1),",",a(2,2),",",a(2,3),"]"print*,"]"print*,"Setting a(1,2)=a(1,2)+3"a(1,2)=a(1,2)+3end subroutinefooend modulemod

and wrap it usingf2py-c-mmoddatamoddata.f90.

In Python:

>>>importmoddata>>>print(moddata.mod.__doc__)i : 'i'-scalarx : 'i'-array(4)a : 'f'-array(2,3)b : 'f'-array(-1,-1), not allocatedfoo()Wrapper for ``foo``.>>>moddata.mod.i=5>>>moddata.mod.x[:2]=[1,2]>>>moddata.mod.a=[[1,2,3],[4,5,6]]>>>moddata.mod.foo() i=           5 x=[           1           2           0           0 ] a=[ [   1.000000     ,   2.000000     ,   3.000000     ] [   4.000000     ,   5.000000     ,   6.000000     ] ] Setting a(1,2)=a(1,2)+3>>>moddata.mod.a# a is Fortran-contiguousarray([[ 1.,  5.,  3.],       [ 4.,  5.,  6.]], dtype=float32)>>>moddata.mod.a.flags.f_contiguousTrue

Allocatable arrays#

F2PY has basic support for Fortran 90 module allocatable arrays.

Consider the following Fortran 90 code:

modulemodreal,allocatable,dimension(:,:)::bcontains  subroutinefoointegerkif(allocated(b))then       print*,"b=["dok=1,size(b,1)print*,b(k,1:size(b,2))enddo       print*,"]"else       print*,"b is not allocated"endif  end subroutinefooend modulemod

and wrap it usingf2py-c-mallocarrallocarr.f90.

In Python:

>>>importallocarr>>>print(allocarr.mod.__doc__)b : 'f'-array(-1,-1), not allocatedfoo()Wrapper for ``foo``.>>>allocarr.mod.foo() b is not allocated>>>allocarr.mod.b=[[1,2,3],[4,5,6]]# allocate/initialize b>>>allocarr.mod.foo() b=[   1.000000       2.000000       3.000000   4.000000       5.000000       6.000000 ]>>>allocarr.mod.b# b is Fortran-contiguousarray([[ 1.,  2.,  3.],       [ 4.,  5.,  6.]], dtype=float32)>>>allocarr.mod.b.flags.f_contiguousTrue>>>allocarr.mod.b=[[1,2,3],[4,5,6],[7,8,9]]# reallocate/initialize b>>>allocarr.mod.foo() b=[   1.000000       2.000000       3.000000   4.000000       5.000000       6.000000   7.000000       8.000000       9.000000 ]>>>allocarr.mod.b=None# deallocate array>>>allocarr.mod.foo() b is not allocated