changing array dimensions in fortran

steabert picture steabert · Mar 23, 2011 · Viewed 26.3k times · Source

There are basically two ways to pass arrays to a subroutine in Fortran 90/95:

PROGRAM ARRAY
INTEGER, ALLOCATABLE :: A(:,:)
INTEGER :: N
ALLOCATE(A(N,N))
CALL ARRAY_EXPLICIT(A,N)
! or
CALL ARRAY_ASSUMED(A)
END PROGRAM ARRAY

SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N,N)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT

SUBROUTINE ARRAY_ASSUMED(A)
INTEGER, ALLOCATABLE :: A(:,:)
N=SIZE(A,1)
! bla bla
END SUBROUTINE ARRAY_ASSUMED

where you need an explicit interface for the second, usually through the use of a module.

From FORTRAN77, I'm used to the first alternative, and I read this is also the most efficient if you pass the whole array.

The nice thing with the explicit shape is that I can also call a subroutine and treat the array as a vector instead of a matrix:

SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N**2)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT

I wondered if there is a nice way to do that kind of thing using the second, assumed shape interface, without copying it.

Answer

janneb picture janneb · Mar 23, 2011

See the RESHAPE intrinsic, e.g.

http://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html

Alternatively, if you want to avoid the copy (in some cases an optimizing compiler might be able to do a reshape without copying, e.g. if the RHS array is not used afterwards, but I wouldn't count on it), as of Fortran 2003 you can assign pointers to targets of different rank, using bounds remapping. E.g. something like

program ptrtest
  real, pointer :: a(:)
  real, pointer :: b(:,:)
  integer :: n = 10
  allocate(a(n**2))
  a = 42
  b (1:n, 1:n) => a
end program ptrtest