由于主程序中的索引重命名,子例程未在假定形状数组中返回正确的数值
Subroutine not returning correct numerical values in assumed shape array due to index renaming in the main program
我的 Fortran 95 子例程的参数是一个假设的形状数组,意图为:
the_subroutine(my_argument)
real, dimension(:,:), intent(inout) :: my_argument
(...)
在主程序中,我有一个可分配数组。我分配它 并重命名索引 。然后我调用子例程并将该(正确分配的)数组传递给子例程:
allocate(the_array( 5:1005 , 5:1005 ))
call the_subroutine(my_argument=the_array)
子例程进行某些计算并用值填充数组。在 子程序结束前的最后一行 ,我检查了一个随机值:
(...)
print*, my_argument(213,126) ! I get 2.873...
end subroutine the_subroutine
然后,在子程序调用后的第一行,我检查子程序是否已将值正确地传递给外部世界,但事实并非如此:
call the_subroutine(my_argument=the_array)
print*, the_array(213,126) ! I get 3.798... A completely different value.
问题是由于在主程序中将数组重新索引为:
allocate(the_array( 5:1005 , 5:1005 ))
其中 max_index - min_index = 1000-1,但是子程序 "sees" 数组在内部就好像我已经声明了正常的方式,即:
allocate(the_array( 1:1000, 1:1000))
或者简单地说,分配(the_array( 1000, 1000 ))
因此,内部数组中的元素(213,126)与主程序数组中的位置不同。有什么简单的方法可以解决这个问题吗?
假定形状数组的默认下限为一。
如果您想要不同的下限,则需要适当地声明数组虚拟参数。
subroutine the_subroutine(my_argument)
real, dimension(5:,5:), intent(inout) :: my_argument
! ^ ^
(延迟形状数组的虚拟参数边界规则不同 - 数组虚拟参数也具有 POINTER 或 ALLOCATABLE 属性。)
使用lbound
将边界传递给子例程:
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
终于找到了解决方法。
首先,如果在 Fortran 2003(或具有非标准扩展的 Fortran 95)中工作,您可以简单地将子例程中假定的形状参数声明为 ALLOCATABLE:
subroutine the_subroutine(my_argument)
real, dimension(:,:), allocatable, intent(inout) :: my_argument
然后子程序"sees"正确地重命名了索引。然而,这在 Fortran 95 标准中是不允许的。
在 Fortran 95 中,我找到的最优雅的方法是使用指针:
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)
并且在子程序中:
subroutine the_subroutine(my_argument)
real, dimension(:,:), pointer :: my_argument
那就完美了。在子例程中,MY_ARGUMENT 被完全视为假设的形状数组。
另一种解决问题的方法:使数组成为派生类型,子例程成为该类型的方法:
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
如您所见,子程序会自动识别正确的尺寸。显然现在子程序只能使用派生类型数组。
我的 Fortran 95 子例程的参数是一个假设的形状数组,意图为:
the_subroutine(my_argument)
real, dimension(:,:), intent(inout) :: my_argument
(...)
在主程序中,我有一个可分配数组。我分配它 并重命名索引 。然后我调用子例程并将该(正确分配的)数组传递给子例程:
allocate(the_array( 5:1005 , 5:1005 ))
call the_subroutine(my_argument=the_array)
子例程进行某些计算并用值填充数组。在 子程序结束前的最后一行 ,我检查了一个随机值:
(...)
print*, my_argument(213,126) ! I get 2.873...
end subroutine the_subroutine
然后,在子程序调用后的第一行,我检查子程序是否已将值正确地传递给外部世界,但事实并非如此:
call the_subroutine(my_argument=the_array)
print*, the_array(213,126) ! I get 3.798... A completely different value.
问题是由于在主程序中将数组重新索引为:
allocate(the_array( 5:1005 , 5:1005 ))
其中 max_index - min_index = 1000-1,但是子程序 "sees" 数组在内部就好像我已经声明了正常的方式,即:
allocate(the_array( 1:1000, 1:1000))
或者简单地说,分配(the_array( 1000, 1000 ))
因此,内部数组中的元素(213,126)与主程序数组中的位置不同。有什么简单的方法可以解决这个问题吗?
假定形状数组的默认下限为一。
如果您想要不同的下限,则需要适当地声明数组虚拟参数。
subroutine the_subroutine(my_argument)
real, dimension(5:,5:), intent(inout) :: my_argument
! ^ ^
(延迟形状数组的虚拟参数边界规则不同 - 数组虚拟参数也具有 POINTER 或 ALLOCATABLE 属性。)
使用lbound
将边界传递给子例程:
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
终于找到了解决方法。
首先,如果在 Fortran 2003(或具有非标准扩展的 Fortran 95)中工作,您可以简单地将子例程中假定的形状参数声明为 ALLOCATABLE:
subroutine the_subroutine(my_argument)
real, dimension(:,:), allocatable, intent(inout) :: my_argument
然后子程序"sees"正确地重命名了索引。然而,这在 Fortran 95 标准中是不允许的。
在 Fortran 95 中,我找到的最优雅的方法是使用指针:
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)
并且在子程序中:
subroutine the_subroutine(my_argument)
real, dimension(:,:), pointer :: my_argument
那就完美了。在子例程中,MY_ARGUMENT 被完全视为假设的形状数组。
另一种解决问题的方法:使数组成为派生类型,子例程成为该类型的方法:
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
如您所见,子程序会自动识别正确的尺寸。显然现在子程序只能使用派生类型数组。