1
votes

The argument of my fortran 95 subroutine is an assumed shape array with intent inout:

the_subroutine(my_argument)
real, dimension(:,:), intent(inout) :: my_argument
(...)

In the main program, I have an allocatable array. I allocate it and also rename indexes. Then I call the subroutine and pass that (correctly allocated) array to the subroutine:

allocate(the_array( 5:1005 , 5:1005 ))
call the_subroutine(my_argument=the_array)

The subroutine does certain calculations and fills the array with values. In the very last line before the end of the subroutine, I check a random value:

(...)
print*, my_argument(213,126) ! I get 2.873...
end subroutine the_subroutine

Then, in the very first line after the subroutine call, I check if the value has been correctly communicated by the subroutine to the outer world, but that is not the case:

call the_subroutine(my_argument=the_array)
print*, the_array(213,126) ! I get 3.798... A completely different value.

The problem arises from having re-indexed the array in the main program as:

allocate(the_array( 5:1005 , 5:1005 ))

where max_index - min_index = 1000-1, but the subroutine "sees" the array internally as if I had declared the normal way, i.e.:

allocate(the_array( 1:1000, 1:1000))

Or simply, allocate(the_array( 1000, 1000 ))

Therefore, the element (213,126) in the internal array is in another location as in the main program array. Is there any easy way out of this?

4
my_argument is assumed shape. A procedure with assumed shape arguments requires an explicit interface in the calling scope. Does the_subroutine have such an interface?IanH
@IanH It is inside a module, so it does nor require any interface. I think.Mephisto
Do you have INTENT(INOUT) on the subroutine? (You may need to show more code)Holmz
@Holmz Yes, I have it as you can see.Mephisto
If it is ALLOCATABLE in the main, then should it also be ALLOCATABLE in the subroutine?Holmz

4 Answers

2
votes

The default lower bound for an assumed shape array is one.

If you want a different lower bound, then you need to declare the array dummy argument appropriately.

subroutine the_subroutine(my_argument)
  real, dimension(5:,5:), intent(inout) :: my_argument
!                 ^  ^

(The rules for the bounds of the dummy argument are different for deferred shape arrays - array dummy arguments which also have the POINTER or ALLOCATABLE attributes.)

1
votes

use lbound to pass the bounds to the subroutine:

  implicit none
  real,allocatable:: x(:,:)
  allocate(x(5:10,5:10))
  call sub(x,lbound(x))
  write(*,*)'o',x(5,5)
  contains

  subroutine sub(x,lb)
  implicit none
  integer lb(2)
  real, dimension(lb(1):,lb(2):)::x
  x(5,5)=42.
  end subroutine
  end

o 42.0000

0
votes

Finally, I found the solution.

First, if working in Fortran 2003 (or Fortran 95 with non-standard extensions), you may simply declare the assumed shape argument in the subroutine as ALLOCATABLE:

subroutine the_subroutine(my_argument)
real, dimension(:,:), allocatable, intent(inout) :: my_argument

Then the subroutine "sees" the renamed index correctly. However this is not allowed in the Fortran 95 standard.

In Fortran 95, the most elegant way I found for this is by making use of a pointer:

program example
implicit none
real, dimension(:,:), allocatable, target  :: the_array
real, dimension(:,:),              pointer :: the_pointer
[...]
allocate(the_array(5:1005,5:1005))
the_pointer => the_array
call the_subroutine(my_argument=the_pointer)

And in the subroutine:

subroutine the_subroutine(my_argument)
real, dimension(:,:), pointer :: my_argument

Then it works perfectly. Inside the subroutine, MY_ARGUMENT is treated exactly as if it was an assumed shape array.

0
votes

another approach to the problem: make the array a derived type and the subroutine a method of the type:

  module example
  implicit none
  type a
  real, allocatable::y(:,:)
  end type a
  contains
  subroutine sub(this)
  type(a)::this
  write(*,*)lbound(this%y)
  end subroutine
  end module

  program p
  use example
  implicit none
  type (a)  my_array
  allocate(my_array%y(5:6,7:8))
  call sub(my_array)
  deallocate(my_array%y)
  allocate(my_array%y(2:3,1:2))
  call sub(my_array)
  end

5 7

2 1

as you see now the subroutine automatically knows the correct dimensions. Obviously now the subroutine can only work with the derived type array.