Movatterモバイル変換


[0]ホーム

URL:


SciPy

Using F2PY bindings in Python

All wrappers for Fortran/C routines, common blocks, or for Fortran90 module data generated by F2PY are exposed to Python asfortrantype objects. Routine wrappers are callablefortran type objectswhile wrappers to Fortran data have attributes referring to dataobjects.

Allfortran type object have attribute_cpointer that containsCObject referring to the C pointer of the corresponding Fortran/Cfunction or variable in C level. Such CObjects can be used as acallback argument of F2PY generated functions to bypass Python C/APIlayer of calling Python functions from Fortran or C when thecomputational part of such functions is implemented in C or Fortranand wrapped with F2PY (or any other tool capable of providing CObjectof a function).

Consider a Fortran 77 fileftype.f:

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

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

In Python:

>>>importftype>>>printftype.__doc__This module 'ftype' is auto-generated with f2py (version:2.28.198-1366).Functions:  foo(n=13)COMMON blocks:  /data/ a,x(3).>>>type(ftype.foo),type(ftype.data)(<type 'fortran'>, <type '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.],'f')

Scalar arguments

In general, a scalar argument of a F2PY generated wrapper function canbe ordinary Python scalar (integer, float, complex number) as well asan arbitrary sequence object (list, tuple, array, string) ofscalars. In the latter case, the first element of the sequence objectis passed to Fortran routine as a scalar argument.

Note that when type-casting is required and there is possible loss ofinformation (e.g. when type-casting float to integer or complex tofloat), F2PY does not raise any exception. In complex to realtype-casting only the real part of a complex number is used.

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

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>>>printscalar.foo.__doc__foo - Function signature:  foo(a,b)Required arguments:  a : input float  b : 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.>>>printa,b# note that only b is changed in situ2 4

String arguments

F2PY generated wrapper functions accept (almost) any Python object asa string argument,str is applied for non-string objects.Exceptions are NumPy arrays that must have type code'c' or'1' when used as string arguments.

A string can have arbitrary length when using it as a string argumentto F2PY generated wrapper function. If the length is greater thanexpected, the string is truncated. If the length is smaller thatexpected, additional memory is allocated and filled with\0.

Because Python strings are immutable, anintent(inout) argumentexpects an array version of a string in order toin situ changes tobe 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>>>printmystring.foo.__doc__foo - Function signature:  foo(a,b,c,d)Required arguments:  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')>>>importnumpy>>>a=numpy.array('123')>>>b=numpy.array('123')>>>c=numpy.array('123')>>>d=numpy.array('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.tostring(),b.tostring(),c.tostring(),d.tostring()('123', 'B23', '123', 'D23')

Array arguments

In general, array arguments of F2PY generated wrapper functions acceptarbitrary sequences that can be transformed to NumPy array objects.An exception isintent(inout) array arguments that always must beproper-contiguous and have proper type, otherwise an exception israised. Another exception isintent(inplace) array arguments thatattributes will be changed in-situ if the argument has different typethan expected (seeintent(inplace) attribute for moreinformation).

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

There are two types of proper-contiguous NumPy arrays:

  • Fortran-contiguous arrays when data is stored column-wise,i.e. indexing of data as stored in memory starts from the lowestdimension;
  • C-contiguous or simply contiguous arrays when data is storedrow-wise, i.e. indexing of data as stored in memory starts from thehighest dimension.

For one-dimensional arrays these notions coincide.

For example, a 2x2 arrayA is Fortran-contiguous if its elementsare stored in memory in the following order:

A[0,0]A[1,0]A[0,1]A[1,1]

and C-contiguous if the order is as follows:

A[0,0]A[0,1]A[1,0]A[1,1]

To test whether an array is C-contiguous, use.iscontiguous()method of NumPy arrays. To test for Fortran contiguity, allF2PY generated extension modules provide a functionhas_column_major_storage(<array>). This function is equivalent to<array>.flags.f_contiguous but more efficient.

Usually there is no need to worry about how the arrays are stored inmemory and whether the wrapped functions, being either Fortran or Cfunctions, assume one or another storage order. F2PY automaticallyensures that wrapped functions get arguments with proper storageorder; the corresponding algorithm is designed to make copies ofarrays only when absolutely necessary. However, when dealing with verylarge multidimensional input arrays with sizes close to the size ofthe physical memory in your computer, then a care must be taken to usealways proper-contiguous and proper type arguments.

To transform input arrays to column major storage order before passingthem to Fortran routines, use a functionas_column_major_storage(<array>) that is provided by all F2PYgenerated extension modules.

Consider 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)+1D0ENDDODOI=1,NA(I,1)=A(I,1)-1D0ENDDOENDCENDOFFILEARRAY.F

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

In Python:

>>>importarr>>>fromnumpyimportarray>>>printarr.foo.__doc__foo - Function signature:  a = foo(a,[overwrite_a])Required arguments:  a : input rank-2 array('d') with bounds (n,m)Optional arguments:  overwrite_a := 0 input intReturn objects:  a : rank-2 array('d') with bounds (n,m)>>>a=arr.foo([[1,2,3],...[4,5,6]])copied an array using PyArray_CopyFromObject: size=6, elsize=8>>>printa[[ 1.  3.  4.] [ 3.  5.  6.]]>>>a.iscontiguous(),arr.has_column_major_storage(a)(0, 1)>>>b=arr.foo(a)# even if a is proper-contiguous...# and has proper type, a copy is made...# forced by intent(copy) attribute...# to preserve its original contents...copied an array using copy_ND_array: size=6, elsize=8>>>printa[[ 1.  3.  4.] [ 3.  5.  6.]]>>>printb[[ 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...>>>printa[[ 1.  4.  5.] [ 2.  5.  6.]]>>>printb[[ 1.  4.  5.] [ 2.  5.  6.]]>>>aisb# a and b are actually the same objects1>>>printarr.foo([1,2,3])# different rank arrays are allowedcopied an array using PyArray_CopyFromObject: size=3, elsize=8[ 1.  1.  2.]>>>printarr.foo([[[1],[2],[3]]])copied an array using PyArray_CopyFromObject: size=3, elsize=8[ [[ 1.]  [ 3.]  [ 4.]]]>>>>>># Creating arrays with column major data storage order:...>>>s=arr.as_column_major_storage(array([[1,2,3],[4,5,6]]))copied an array using copy_ND_array: size=6, elsize=4>>>arr.has_column_major_storage(s)1>>>prints[[1 2 3] [4 5 6]]>>>s2=arr.as_column_major_storage(s)>>>s2iss# an array with column major storage order               # is returned immediately1

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*8RCf2pyintent(out)rR=0D0DOI=-5,5R=R+FUN(I)ENDDOENDCENDOFFILECALLBACK.F

and wrap it usingf2py-c-mcallbackcallback.f.

In Python:

>>>importcallback>>>printcallback.foo.__doc__foo - Function signature:  r = foo(fun,[fun_extra_args])Required arguments:  fun : call-back functionOptional arguments:  fun_extra_args := () input tupleReturn objects:  r : floatCall-back functions:  def fun(i): return r  Required arguments:    i : input int  Return objects:    r : float>>>deff(i):returni*i...>>>printcallback.foo(f)110.0>>>printcallback.foo(lambdai:1)11.0

In the above example F2PY was able to guess accurately the signatureof a call-back function. However, sometimes F2PY cannot establish thesignature as one would wish and then the signature of a call-backfunction must be modified in the signature file manually. Namely,signature files may contain special modules (the names of such modulescontain a substring__user__) that collect various signatures ofcall-back functions. Callback arguments in routine signatures haveattributeexternal (see alsointent(callback) attribute). Torelate a callback argument and its signature in__user__ moduleblock, useuse statement as illustrated below. The same signatureof a callback argument can be referred in different routinesignatures.

We use the same Fortran 77 code as in previous example but nowwe’ll 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, build the extension module usingf2py-ccallback2.pyfcallback.f.

An example Python session would be identical to the previous exampleexcept that argument names would differ.

Sometimes a Fortran package may require that users provide routinesthat the package will use. F2PY can construct an interface to suchroutines so that Python functions could be called from Fortran.

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

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

It is expected that functionfunc has been definedexternally. In order to use a Python function asfunc, it musthave an attributeintent(callback) (it must be specified beforetheexternal 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.71828175,   7.38905621,  20.08553696,  54.59814835])

The function is included as an argument to the python function call tothe Fortran subroutine even though it wasnot in the Fortran subroutine argumentlist. The “external” refers to the C function generated by f2py, not the pythonfunction itself. The python function must be supplied to the C function.

The callback function may also be explicitly set in the module.Then it is not necessary to pass the function in the argument list tothe Fortran function. This may be desired if the Fortran function callingthe python callback function is itself called by another Fortran function.

Consider the following Fortran 77 subroutine:

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

and wrap it usingf2py-c-mpfromfextcallback.f.

In Python:

>>>importpfromf>>>pfromf.f2()Traceback (most recent call last):  File"<stdin>", line1, in?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>>>

Resolving arguments to call-back functions

F2PY generated interface is very flexible with respect to call-backarguments. For each call-back argument an additional optionalargument<name>_extra_args is introduced by F2PY. This argumentcan be used to pass extra arguments to user provided call-backarguments.

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

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 Cfunction calls 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, hereq=m-p.
  • Ifp>m thengun(e_1,...,e_m) is called.
  • Ifn+p is less than the number of required arguments togunthen an exception is raised.

The functiongun may return any number of objects as a tuple. Thenfollowing 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 routinesignature block. Common blocks are visible by all Fortran codes linkedwith the current extension module, but not to other extension modules(this restriction is due to how Python imports shared libraries). InPython, the F2PY wrappers tocommon blocks arefortran typeobjects that have (dynamic) attributes related to data members ofcommon blocks. When accessed, these attributes return as NumPy arrayobjects (multidimensional arrays are Fortran-contiguous) thatdirectly link to data members in common blocks. Data members can bechanged 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>>>printcommon.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() I= 5 X=[ 0 2 0 0] A=[ [  1.,  2.,  3.] [  4.,  5.,  6.] ]>>>common.data.a[1]=45>>>common.foo() I= 5 X=[ 0 2 0 0] A=[ [  1.,  2.,  3.] [  45.,  45.,  45.] ]>>>common.data.a# a is Fortran-contiguousarray([[  1.,   2.,   3.],       [ 45.,  45.,  45.]],'f')

Fortran 90 module data

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

Consider the following Fortran 90 code:

modulemodintegeriinteger::x(4)real,dimension(2,3)::areal,allocatable,dimension(:,:)::bcontainssubroutinefoointegerkprint*,"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)+3endsubroutinefooendmodulemod

and wrap it usingf2py-c-mmoddatamoddata.f90.

In Python:

>>>importmoddata>>>printmoddata.mod.__doc__i - 'i'-scalarx - 'i'-array(4)a - 'f'-array(2,3)foo - Function signature:  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.]],'f')

Allocatable arrays

F2PY has basic support for Fortran 90 module allocatable arrays.

Consider the following Fortran 90 code:

modulemodreal,allocatable,dimension(:,:)::bcontainssubroutinefoointegerkif(allocated(b))thenprint*,"b=["dok=1,size(b,1)print*,b(k,1:size(b,2))enddoprint*,"]"elseprint*,"b is not allocated"endifendsubroutinefooendmodulemod

and wrap it usingf2py-c-mallocarrallocarr.f90.

In Python:

>>>importallocarr>>>printallocarr.mod.__doc__b - 'f'-array(-1,-1), not allocatedfoo - Function signature:  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.]],'f')>>>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

Table Of Contents

Previous topic

Signature file

Next topic

Using F2PY

Quick search

  • © Copyright 2008-2018, The SciPy community.
  • Last updated on Jul 24, 2018.
  • Created usingSphinx 1.6.6.

[8]ページ先頭

©2009-2025 Movatter.jp