Subroutine not returning correct numerical values

2019-08-02 18:39发布

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条回答
闹够了就滚
2楼-- · 2019-08-02 19:03

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.)

查看更多
祖国的老花朵
3楼-- · 2019-08-02 19:03

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.

查看更多
等我变得足够好
4楼-- · 2019-08-02 19:12

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

查看更多
Animai°情兽
5楼-- · 2019-08-02 19:21

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.

查看更多
登录 后发表回答