ye-ti-800
ye-ti-800

Reputation: 218

f2py: Pre-allocating arrays as input for Fortran subroutine

Here is a minimalized snippet from a Fortran subroutine that i want to call in python. The task is very simple. The first time the code is called a value is assigned to the first entry of the array iwk. The subroutine is then called a second time, the expected behaviour would be that the assigned entry is unchanged.

subroutine testf2py (md,iwk,ndp,nip)

  implicit none

  integer ndp
!f2py intent(in) :: ndp
  integer nip
!f2py intent(in) :: nip
  integer iwk(31*ndp + nip)
!f2py intent(in) :: iwk     
  integer md
!f2py intent(in) :: md

  if ( md == 1 ) then
    iwk(1) = ndp
  end if  

  print*, iwk(1)

end

(first call: md=1, subsequent calls: md>1). The array iwk should be allocated in python and the values in the array should be untouched in between calls. Calling this function in Fortran this works without problem:

program main
  implicit none
  integer :: i, ndp, nip  
  integer, allocatable :: iwk(:)

  ndp = 10
  nip = 10

  allocate(iwk(31*ndp+nip))  

  call test (1, iwk, ndp, nip)
  call test (2, iwk, ndp, nip)

end program main

Output of the 2 calls:

10
10

In python, after compiling the subroutine with f2py, it does not work. The values in iwk change:

import testf2py
import numpy as np

ndp = 10
nip = 10

iwk = np.empty([31*ndp+nip,], dtype=int, order='F')

testf2py.test(1, iwk, ndp, nip)
testf2py.test(2, iwk, ndp, nip)

Output of the 2 calls:

10
1686728152   

The second number is random.

I tried allocating in python in different ways, np.empty_like, np.zeros. Did not help. I assumed that the problem lies with how to allocate the iwk array (hence the question title), but not sure. What do i have to change to make it work? Preferably, i want to change only the python script and leave the Fortran code as it is.

Upvotes: 3

Views: 1603

Answers (1)

You tell python that the array is intent(in):

  integer iwk(31*ndp + nip)
!f2py intent(in) :: iwk  

You are not allowed to change it and expect the change will be retained. Probably you want intent(inout).

Even better, forget !f2py intent and use directly:

  integer, intent(inout) :: iwk(31*ndp + nip)

and the compiler will tell you you are wrong if you try to modify an intent(in) variable.

Be careful to use the correct dtype in Python, it must correspond to the Fortran type. Most often Fortran integer is equivalent to int32, but it might be int64 sometimes. You can control that in Fortran too (integer*4, integer(int32),...).

Upvotes: 3

Related Questions