Ryan Walker
Ryan Walker

Reputation: 3286

Expose both "in,out" and "inplace" versions of subroutine with f2py

I am using f2py to integrate some Fortran77 routines into my python module but can't settle on a good way to solve the following problem. My Fortran routines destroy all input arrays during the course of computation. I'd like to offer the user both the option to use the memory efficient "inplace" behaviour of these routines as well as the option to execute the routine without destroying their Python side data.

I can think of two ways to do this:

  1. If user does not want inputs destroyed, backup all the input arrays in Python before calling my Fortran routine. Compile Fortran routines using "intent(inplace)" declaration in my signature. On exit, I can restore the inputs using the backup copy.
  2. Compile the same Fortran routine against two different signature files, one with "intent(inplace)" and one with "intent(in,out)" (or just "intent(in)" depending on the situation). Then in python I can import both flavors of the routines into my module.

I don't like either approach very much because they both seem non-pythonic. Is there a better way to approach this problem? Is there simpler way to implement approach 2, e.g. can I compile the same routine with two different signatures into a single shared object module?

Here's an example (based on this documentation).

Fortran routine fib.f:

C FILE: FIB.F
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE FIB.F

I generate my signature file using f2py fib.f -m fib.pyf then I add some intent declarations:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module fib ! in
    interface  ! in :fib
        subroutine fib(a,n) ! in :fib:fib.f
            real*8 dimension(n), intent(inout) :: a
            integer, optional,check(len(a)>=n),depend(a) :: n=len(a)
        end subroutine fib
    end interface
end python module fib

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

I compile with f2py -c fib.pyf fib.f and then I can run my routine in place:

>>> import numpy as np
>>> from fib import fib
>>> A = np.zeros(10, dtype=float)
>>> fib(A)
>>> print(A)
[  0.   1.   1.   2.   3.   5.   8.  13.  21.  34.]

To change this example to in/out mode, I just change my intent declaration in "fib.pyf" to intent(in,out).

Upvotes: 1

Views: 362

Answers (1)

Ryan Walker
Ryan Walker

Reputation: 3286

I ended up using the fortranname declaration in the signature files to generate both inplace and in,out flavors of the same subroutine. Here's the signature file:

    !    -*- f90 -*-
    ! Note: the context of this file is case sensitive.

    python module fib ! in
            interface  ! in :fib
                subroutine fib_inplace(a,n) ! in :fib:fib.f
                    fortranname fib
                    real*8 dimension(n), intent(inout) :: a
                    integer, optional,check(len(a)>=n),depend(a) :: n=len(a)
                end subroutine fib
                subroutine fib(a,n) ! in :fib:fib.f
                    fortranname fib
                    real*8 dimension(n), intent(copy, in,out) :: a
                    integer, optional,check(len(a)>=n),depend(a) :: n=len(a)
                end subroutine fib
            end interface
    end python module fib

Note: to actually prevent the second routine from modifying the Python input I had to also add copy to the intent directive.

Upvotes: 1

Related Questions